hermes-web/components/elements/redemption.tsx

275 lines
11 KiB
TypeScript

import axios from "axios";
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Label } from "../ui/label";
import { HelpCircleIcon, Trash2Icon } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "../ui/tooltip"
interface Redemption {
id: string | undefined
redemptionId: string | undefined
actionName: string
numbering: number,
edit: boolean
showEdit: boolean
isNew: boolean
actions: string[]
twitchRedemptions: { id: string, title: string }[]
adder: (id: string, actionName: string, redemptionId: string, order: number) => void
remover: (redemption: { id: string, redemptionId: string, actionName: string, order: number }) => void
}
const OBSRedemption = ({
id,
redemptionId,
actionName,
numbering,
edit,
showEdit,
isNew,
actions,
twitchRedemptions,
adder,
remover
}: Redemption) => {
const [actionOpen, setActionOpen] = useState(false)
const [redemptionOpen, setRedemptionOpen] = useState(false)
const [twitchRedemption, setTwitchRedemption] = useState<{ id: string, title: string } | undefined>(undefined)
const [action, setAction] = useState<string | undefined>(actionName)
const [order, setOrder] = useState<number>(numbering)
const [isEditable, setIsEditable] = useState(edit)
const [oldData, setOldData] = useState<{ r: { id: string, title: string } | undefined, a: string | undefined, o: number } | undefined>(undefined)
useEffect(() => {
setTwitchRedemption(twitchRedemptions.find(r => r.id == redemptionId))
}, [])
function Save() {
// TODO: validation
if (!isNew && !id)
return
if (!action || !twitchRedemption)
return
if (isNew) {
axios.post("/api/settings/redemptions", {
actionName: action,
redemptionId: twitchRedemption?.id,
order: order,
state: true
}).then(d => {
adder(d.data.id, action, twitchRedemption.id, order)
setAction(undefined)
setTwitchRedemption(undefined)
setOrder(0)
})
} else {
axios.put("/api/settings/redemptions", {
id: id,
actionName: action,
redemptionId: twitchRedemption?.id,
order: order,
state: true
}).then(d => {
setIsEditable(false)
})
}
}
function Cancel() {
if (!oldData)
return
setAction(oldData.a)
setTwitchRedemption(oldData.r)
setOrder(oldData.o)
setIsEditable(false)
setOldData(undefined)
}
function Delete() {
axios.delete("/api/settings/redemptions?id=" + id)
.then(d => {
remover(d.data)
})
}
return (
<div
className="bg-orange-300 p-5 border-2 border-orange-400 rounded-lg w-[830px]">
<div
className="pb-4">
<Label
className="mr-2"
htmlFor="redemption">
Twitch Redemption
</Label>
{!isEditable &&
<Input
className="inline-block w-[290px] justify-between"
name="redemption"
value={twitchRedemption?.title}
readOnly />
|| isEditable &&
<Popover
open={redemptionOpen}
onOpenChange={setRedemptionOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={actionOpen}
className="w-[290px] justify-between"
>{!twitchRedemption ? "Select one..." : twitchRedemption.title}</Button>
</PopoverTrigger>
<PopoverContent>
<Command>
<CommandInput
placeholder="Filter redemptions..."
autoFocus={true} />
<CommandList>
<CommandEmpty>No redemption found.</CommandEmpty>
<CommandGroup>
{twitchRedemptions.map((redemption) => (
<CommandItem
value={redemption.title}
key={redemption.id}
onSelect={(value) => {
setTwitchRedemption(twitchRedemptions.find(v => v.title.toLowerCase() == value.toLowerCase()))
setRedemptionOpen(false)
}}>
{redemption.title}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
}
<Label
className="ml-10 mr-2"
htmlFor="action">
Action
</Label>
{!isEditable &&
<Input
className="inline-block w-[290px] justify-between"
name="action"
value={action}
readOnly />
|| isEditable &&
<Popover
open={actionOpen}
onOpenChange={setActionOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={actionOpen}
className="w-[290px] justify-between">
{!action ? "Select one..." : action}
</Button>
</PopoverTrigger>
<PopoverContent>
<Command>
<CommandInput
placeholder="Filter actions..."
autoFocus={true} />
<CommandList>
<CommandEmpty>No action found.</CommandEmpty>
<CommandGroup>
{actions.map((action) => (
<CommandItem
value={action}
key={action}
onSelect={(value) => {
let a = actions.find(v => v == action)
if (a)
setAction(a)
setActionOpen(false)
}}>
{action}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
}
</div>
<div
className="pb-4">
<Label
className="mr-2"
htmlFor="order">
Order
</Label>
<Input
className="inline-block w-[300px]"
id="name"
placeholder="Enter an order number for this action"
onChange={e => setOrder(e.target.value.length == 0 ? 0 : parseInt(e.target.value))}
value={order}
readOnly={!isEditable} />
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircleIcon
className="inline-block ml-3"/>
</TooltipTrigger>
<TooltipContent>
<p>This decides when this action will be done relative to other actions for this Twitch redemption.<br/>
Action start from lowest to highest order number.<br/>
Equal order numbers cannot be guaranteed proper order.</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div>
{isEditable &&
<Button
className="m-3"
onClick={() => Save()}>
{isNew ? "Add" : "Save"}
</Button>
}
{isEditable && !isNew &&
<Button
className="m-3"
onClick={() => Cancel()}>
Cancel
</Button>
}
{showEdit && !isEditable &&
<Button
className="m-3"
onClick={() => {
setOldData({ a: actionName, r: twitchRedemption, o: order })
setIsEditable(true)
}}>
Edit
</Button>
}
{!isEditable &&
<Button
className="m-3 bg-red-500 hover:bg-red-600 align-bottom"
onClick={() => Delete()}>
<Trash2Icon />
</Button>
}
</div>
</div>
);
}
export default OBSRedemption;