mirror of
https://github.com/hex248/sprint.git
synced 2026-02-07 18:23:03 +00:00
put status options in a dropdown
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
type OrganisationMemberResponse,
|
||||
type OrganisationResponse,
|
||||
} from "@issue/shared";
|
||||
import { ChevronDown, ChevronUp, Plus, X } from "lucide-react";
|
||||
import { ChevronDown, ChevronUp, EllipsisVertical, Plus, X } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { AddMemberDialog } from "@/components/add-member-dialog";
|
||||
@@ -16,6 +16,12 @@ import { Button } from "@/components/ui/button";
|
||||
import ColourPicker from "@/components/ui/colour-picker";
|
||||
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
@@ -207,6 +213,7 @@ function OrganisationsDialog({
|
||||
newStatuses[trimmed] = newStatusColour;
|
||||
await updateStatuses(newStatuses);
|
||||
setNewStatusName("");
|
||||
setNewStatusColour(DEFAULT_STATUS_COLOUR);
|
||||
setIsCreatingStatus(false);
|
||||
setStatusError(null);
|
||||
};
|
||||
@@ -421,42 +428,58 @@ function OrganisationsDialog({
|
||||
/>
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="dummy"
|
||||
size="none"
|
||||
disabled={index === 0}
|
||||
onClick={() => void moveStatus(status, "up")}
|
||||
aria-label="Move status up"
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
asChild
|
||||
size={"sm"}
|
||||
noStyle
|
||||
className="hover:opacity-80 cursor-pointer"
|
||||
>
|
||||
<ChevronUp className="size-5 text-muted-foreground" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="dummy"
|
||||
size="none"
|
||||
disabled={
|
||||
index === Object.keys(statuses).length - 1
|
||||
}
|
||||
onClick={() =>
|
||||
void moveStatus(status, "down")
|
||||
}
|
||||
aria-label="Move status down"
|
||||
<EllipsisVertical className="size-4 text-foreground" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
className="bg-background"
|
||||
>
|
||||
<ChevronDown className="size-5 text-muted-foreground" />
|
||||
</Button>
|
||||
{Object.keys(statuses).length > 1 && (
|
||||
<Button
|
||||
variant="dummy"
|
||||
size="none"
|
||||
onClick={() =>
|
||||
<DropdownMenuItem
|
||||
disabled={index === 0}
|
||||
onSelect={() =>
|
||||
void moveStatus(status, "up")
|
||||
}
|
||||
className="hover:bg-primary-foreground"
|
||||
>
|
||||
<ChevronUp className="size-4 text-muted-foreground" />
|
||||
Move up
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
disabled={
|
||||
index ===
|
||||
Object.keys(statuses).length - 1
|
||||
}
|
||||
onSelect={() =>
|
||||
void moveStatus(status, "down")
|
||||
}
|
||||
className="hover:bg-primary-foreground"
|
||||
>
|
||||
<ChevronDown className="size-4 text-muted-foreground" />
|
||||
Move down
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
variant="destructive"
|
||||
disabled={
|
||||
Object.keys(statuses).length <= 1
|
||||
}
|
||||
onSelect={() =>
|
||||
handleRemoveStatusClick(status)
|
||||
}
|
||||
aria-label="Remove status"
|
||||
className="hover:bg-destructive/10"
|
||||
>
|
||||
<X className="size-5 text-destructive" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<X className="size-4" />
|
||||
Remove
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
@@ -564,19 +587,22 @@ function OrganisationsDialog({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Remove Status</DialogTitle>
|
||||
</DialogHeader>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Are you sure you want to remove the "{statusToRemove}" status? Which status
|
||||
would you like issues with this status to be set to?
|
||||
Are you sure you want to remove the{" "}
|
||||
{statusToRemove ? (
|
||||
<StatusTag status={statusToRemove} colour={statuses[statusToRemove]} />
|
||||
) : null}{" "}
|
||||
status? Which status would you like issues with this status to be set to?
|
||||
</p>
|
||||
<Select value={reassignToStatus} onValueChange={setReassignToStatus}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectTrigger className="w-min">
|
||||
<SelectValue placeholder="Select status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectContent side={"bottom"} position="popper" align="start">
|
||||
{Object.keys(statuses)
|
||||
.filter((s) => s !== statusToRemove)
|
||||
.map((status) => (
|
||||
|
||||
@@ -14,28 +14,34 @@ function DropdownMenuPortal({ ...props }: React.ComponentProps<typeof DropdownMe
|
||||
function DropdownMenuTrigger({
|
||||
className,
|
||||
size = "default",
|
||||
noStyle = false,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger> & {
|
||||
size?: "sm" | "default";
|
||||
noStyle?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Trigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"cursor-pointer border data-[placeholder]:text-muted-foreground",
|
||||
"[&_svg:not([class*='text-'])]:text-foreground",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
||||
"aria-invalid:border-destructive dark:hover:bg-muted/40",
|
||||
"flex w-fit items-center justify-between gap-2 border",
|
||||
"bg-transparent px-3 py-2 text-sm whitespace-nowrap",
|
||||
"shadow-xs outline-none disabled:cursor-not-allowed",
|
||||
"disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8",
|
||||
"*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex",
|
||||
"*:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
|
||||
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
className={
|
||||
noStyle
|
||||
? cn(className)
|
||||
: cn(
|
||||
"cursor-pointer border data-[placeholder]:text-muted-foreground",
|
||||
"[&_svg:not([class*='text-'])]:text-foreground",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
||||
"aria-invalid:border-destructive dark:hover:bg-muted/40",
|
||||
"flex w-fit items-center justify-between gap-2 border",
|
||||
"bg-transparent px-3 py-2 text-sm whitespace-nowrap",
|
||||
"shadow-xs outline-none disabled:cursor-not-allowed",
|
||||
"disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8",
|
||||
"*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex",
|
||||
"*:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
|
||||
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user