Add refresh functionality and duplicate fight feature in FightEditRow component
This commit is contained in:
@ -296,6 +296,7 @@
|
||||
bind:groups={data.groups}
|
||||
event={data.event}
|
||||
onupdate={(update) => (data.fights = data.fights.map((v) => (v.id === update.id ? update : v)))}
|
||||
{refresh}
|
||||
></FightEditRow>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
<script lang="ts">
|
||||
import type { EventFight, EventFightEdit, ResponseGroups, SWEvent } from "@type/event";
|
||||
import { Button } from "@components/ui/button";
|
||||
import { EditIcon, MenuIcon, GroupIcon } from "lucide-svelte";
|
||||
import { EditIcon, CopyIcon } from "lucide-svelte";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@components/ui/dialog";
|
||||
import FightEdit from "@components/moderator/components/FightEdit.svelte";
|
||||
import type { Team } from "@components/types/team";
|
||||
import { fightRepo } from "@components/repo/fight";
|
||||
import { eventRepo } from "@components/repo/event";
|
||||
|
||||
let { fight, teams, groups = $bindable(), event, onupdate }: { fight: EventFight; teams: Team[]; groups: ResponseGroups[]; event: SWEvent; onupdate: (update: EventFight) => void } = $props();
|
||||
let {
|
||||
fight,
|
||||
teams,
|
||||
groups = $bindable(),
|
||||
event,
|
||||
onupdate,
|
||||
refresh,
|
||||
}: { fight: EventFight; teams: Team[]; groups: ResponseGroups[]; event: SWEvent; onupdate: (update: EventFight) => void; refresh: () => void } = $props();
|
||||
|
||||
let editOpen = $state(false);
|
||||
let duplicateOpen = $state(false);
|
||||
|
||||
async function handleSave(fightData: EventFightEdit) {
|
||||
let f = await $fightRepo.updateFight(event.id, fight.id, {
|
||||
@ -23,6 +32,18 @@
|
||||
|
||||
editOpen = false;
|
||||
}
|
||||
|
||||
async function handlyCopy(fightData: EventFightEdit) {
|
||||
await $eventRepo.createFight(event.id.toString(), {
|
||||
...fightData,
|
||||
blueTeam: fightData.blueTeam.id,
|
||||
redTeam: fightData.redTeam.id,
|
||||
});
|
||||
|
||||
refresh();
|
||||
|
||||
duplicateOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@ -46,4 +67,24 @@
|
||||
</FightEdit>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Dialog bind:open={duplicateOpen}>
|
||||
<DialogTrigger>
|
||||
<Button variant="ghost" size="icon">
|
||||
<CopyIcon />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Fight duplizieren</DialogTitle>
|
||||
<DialogDescription>Hier kannst du die Daten des duplizierten Fights ändern</DialogDescription>
|
||||
</DialogHeader>
|
||||
<FightEdit {fight} {teams} bind:groups {event} onSave={handlyCopy}>
|
||||
{#snippet actions(dirty, submit)}
|
||||
<DialogFooter>
|
||||
<Button onclick={submit}>Speichern</Button>
|
||||
</DialogFooter>
|
||||
{/snippet}
|
||||
</FightEdit>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
<CommandEmpty>No Players found :(</CommandEmpty>
|
||||
<CommandGroup heading="Players">
|
||||
{#each $players
|
||||
.filter((v) => v.name.includes(playerSearch))
|
||||
.filter((v) => v.name.toLowerCase().includes(playerSearch.toLowerCase()))
|
||||
.filter((v, i) => i < 50)
|
||||
.filter((v) => !referees.some((k) => k.uuid === v.uuid)) as player (player.uuid)}
|
||||
<CommandItem value={player.name} onSelect={() => addReferee(player.uuid)} keywords={[player.uuid]}>{player.name}</CommandItem>
|
||||
|
||||
@ -24,14 +24,15 @@
|
||||
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
||||
import { CalendarIcon } from "lucide-svelte";
|
||||
import { cn } from "@components/utils";
|
||||
import type {ZonedDateTime} from "@internationalized/date";
|
||||
import { fromDate, type ZonedDateTime } from "@internationalized/date";
|
||||
import Input from "../input/input.svelte";
|
||||
|
||||
let {
|
||||
value = $bindable(),
|
||||
onChange
|
||||
onChange,
|
||||
}: {
|
||||
value: ZonedDateTime
|
||||
onChange?: ((date: ZonedDateTime | undefined) => void) | undefined
|
||||
value: ZonedDateTime;
|
||||
onChange?: ((date: ZonedDateTime | undefined) => void) | undefined;
|
||||
} = $props();
|
||||
|
||||
let isOpen = $state(false);
|
||||
@ -63,13 +64,7 @@
|
||||
|
||||
<Popover bind:open={isOpen}>
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
variant="outline"
|
||||
class={cn(
|
||||
"w-full justify-start text-left font-normal",
|
||||
!value && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<Button variant="outline" class={cn("w-full justify-start text-left font-normal", !value && "text-muted-foreground")}>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
{#if value}
|
||||
{new Intl.DateTimeFormat("de-DE", {
|
||||
@ -86,23 +81,14 @@
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<Input type="datetime-local" value={value.toDate().toISOString().slice(0, 16)} onchange={(e) => handleDateSelect(fromDate(e.target.valueAsDate, "Europe/Berlin"))} />
|
||||
<div class="sm:flex">
|
||||
<Calendar
|
||||
mode="single"
|
||||
bind:value
|
||||
onValueChange={(date) => handleDateSelect(date)}
|
||||
initialFocus
|
||||
/>
|
||||
<Calendar mode="single" bind:value onValueChange={(date) => handleDateSelect(date)} initialFocus />
|
||||
<div class="flex flex-col sm:flex-row sm:h-[300px] divide-y sm:divide-y-0 sm:divide-x">
|
||||
<ScrollArea class="w-64 sm:w-auto">
|
||||
<div class="flex sm:flex-col p-2">
|
||||
{#each [...hours].reverse() as hour}
|
||||
<Button
|
||||
size="icon"
|
||||
variant={value && value.hour === hour ? "default" : "ghost"}
|
||||
class="sm:w-full shrink-0 aspect-square"
|
||||
onclick={() => handleTimeChange("hour", hour)}
|
||||
>
|
||||
<Button size="icon" variant={value && value.hour === hour ? "default" : "ghost"} class="sm:w-full shrink-0 aspect-square" onclick={() => handleTimeChange("hour", hour)}>
|
||||
{hour}
|
||||
</Button>
|
||||
{/each}
|
||||
@ -113,12 +99,12 @@
|
||||
<div class="flex sm:flex-col p-2">
|
||||
{#each Array.from({ length: 60 }, (_, i) => i) as minute}
|
||||
<Button
|
||||
size="icon"
|
||||
variant={value && value.minute === minute ? "default" : "ghost"}
|
||||
class="sm:w-full shrink-0 aspect-square"
|
||||
onclick={() => handleTimeChange("minute", minute)}
|
||||
size="icon"
|
||||
variant={value && value.minute === minute ? "default" : "ghost"}
|
||||
class="sm:w-full shrink-0 aspect-square"
|
||||
onclick={() => handleTimeChange("minute", minute)}
|
||||
>
|
||||
{minute.toString().padStart(2, '0')}
|
||||
{minute.toString().padStart(2, "0")}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@ -1,29 +1,6 @@
|
||||
import Root from "./input.svelte";
|
||||
|
||||
export type FormInputEvent<T extends Event = Event> = T & {
|
||||
currentTarget: EventTarget & HTMLInputElement;
|
||||
};
|
||||
export type InputEvents = {
|
||||
blur: FormInputEvent<FocusEvent>;
|
||||
change: FormInputEvent<Event>;
|
||||
click: FormInputEvent<MouseEvent>;
|
||||
focus: FormInputEvent<FocusEvent>;
|
||||
focusin: FormInputEvent<FocusEvent>;
|
||||
focusout: FormInputEvent<FocusEvent>;
|
||||
keydown: FormInputEvent<KeyboardEvent>;
|
||||
keypress: FormInputEvent<KeyboardEvent>;
|
||||
keyup: FormInputEvent<KeyboardEvent>;
|
||||
mouseover: FormInputEvent<MouseEvent>;
|
||||
mouseenter: FormInputEvent<MouseEvent>;
|
||||
mouseleave: FormInputEvent<MouseEvent>;
|
||||
mousemove: FormInputEvent<MouseEvent>;
|
||||
paste: FormInputEvent<ClipboardEvent>;
|
||||
input: FormInputEvent<InputEvent>;
|
||||
wheel: FormInputEvent<WheelEvent>;
|
||||
};
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Input,
|
||||
Root,
|
||||
//
|
||||
Root as Input,
|
||||
};
|
||||
|
||||
@ -1,42 +1,39 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLInputAttributes } from "svelte/elements";
|
||||
import type { InputEvents } from "./index.js";
|
||||
import { cn } from "$lib/components/utils.js";
|
||||
|
||||
type $$Props = HTMLInputAttributes;
|
||||
type $$Events = InputEvents;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let value: $$Props["value"] = undefined;
|
||||
export { className as class };
|
||||
|
||||
// Workaround for https://github.com/sveltejs/svelte/issues/9305
|
||||
// Fixed in Svelte 5, but not backported to 4.x.
|
||||
export let readonly: $$Props["readonly"] = undefined;
|
||||
import type { HTMLInputAttributes, HTMLInputTypeAttribute } from "svelte/elements";
|
||||
import { cn } from "@components/utils";
|
||||
import { type WithElementRef } from "bits-ui";
|
||||
type InputType = Exclude<HTMLInputTypeAttribute, "file">;
|
||||
type Props = WithElementRef<Omit<HTMLInputAttributes, "type"> & ({ type: "file"; files?: FileList } | { type?: InputType; files?: undefined })>;
|
||||
let { ref = $bindable(null), value = $bindable(), type, files = $bindable(), class: className, ...restProps }: Props = $props();
|
||||
</script>
|
||||
|
||||
<input
|
||||
class={cn(
|
||||
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
bind:value
|
||||
{readonly}
|
||||
on:blur
|
||||
on:change
|
||||
on:click
|
||||
on:focus
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:keydown
|
||||
on:keypress
|
||||
on:keyup
|
||||
on:mouseover
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
on:mousemove
|
||||
on:paste
|
||||
on:input
|
||||
on:wheel|passive
|
||||
{...$$restProps}
|
||||
/>
|
||||
{#if type === "file"}
|
||||
<input
|
||||
bind:this={ref}
|
||||
data-slot="input"
|
||||
class={cn(
|
||||
"selection:bg-primary dark:bg-input/30 selection:text-primary-foreground border-input ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 pt-1.5 text-sm font-medium outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
)}
|
||||
type="file"
|
||||
bind:files
|
||||
bind:value
|
||||
{...restProps}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
bind:this={ref}
|
||||
data-slot="input"
|
||||
class={cn(
|
||||
"border-input bg-background selection:bg-primary dark:bg-input/30 selection:text-primary-foreground ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
)}
|
||||
{type}
|
||||
bind:value
|
||||
{...restProps}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user