change scheduling mode to enum in rounds, change appreance of scheduling
mode picker in competition
This commit is contained in:
parent
592da87c26
commit
95d8988876
16 changed files with 159 additions and 115 deletions
|
@ -26,6 +26,7 @@
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
body: JSON.stringify({ scheduling_mode: schedulingMode })
|
body: JSON.stringify({ scheduling_mode: schedulingMode })
|
||||||
});
|
});
|
||||||
|
console.log("response", response)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -37,13 +38,11 @@
|
||||||
>
|
>
|
||||||
{#each Object.values(SchedulingMode) as mode}
|
{#each Object.values(SchedulingMode) as mode}
|
||||||
<button
|
<button
|
||||||
class={cn(
|
class=
|
||||||
'flex h-full w-full items-center gap-4 rounded-xl border',
|
'flex h-full w-full items-center gap-4 rounded-xl'
|
||||||
schedulingMode === mode && ' border-green-500'
|
|
||||||
)}
|
|
||||||
onclick={() => (schedulingMode = mode)}
|
onclick={() => (schedulingMode = mode)}
|
||||||
>
|
>
|
||||||
<Card class="w-full">
|
<Card class={cn("w-full", schedulingMode === mode && ' border-green-500')}>
|
||||||
<CardHeader class="flex justify-between">
|
<CardHeader class="flex justify-between">
|
||||||
<Label class="text-start">{$_(mode)}</Label>
|
<Label class="text-start">{$_(mode)}</Label>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
7
src/lib/components/rounds/round.svelte
Normal file
7
src/lib/components/rounds/round.svelte
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Round } from '@/lib/server/db/schema/rounds';
|
||||||
|
|
||||||
|
let { round }: { round: Round } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{JSON.stringify(round)}
|
|
@ -1,7 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import CreateRound from './create-round.svelte';
|
|
||||||
|
|
||||||
let { rounds, createForm } = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CreateRound data={{ form: createForm }} />
|
|
|
@ -8,7 +8,6 @@ import * as matches from './schema/matches';
|
||||||
import * as permissions from './schema/permissions';
|
import * as permissions from './schema/permissions';
|
||||||
import * as roles from './schema/roles';
|
import * as roles from './schema/roles';
|
||||||
import * as rounds from './schema/rounds';
|
import * as rounds from './schema/rounds';
|
||||||
import * as schedulingmodes from './schema/schedulingmodes';
|
|
||||||
import * as sessions from './schema/sessions';
|
import * as sessions from './schema/sessions';
|
||||||
import * as teams from './schema/teams';
|
import * as teams from './schema/teams';
|
||||||
import * as users from './schema/users';
|
import * as users from './schema/users';
|
||||||
|
@ -26,7 +25,6 @@ export const db = drizzle(client, {
|
||||||
...permissions,
|
...permissions,
|
||||||
...roles,
|
...roles,
|
||||||
...rounds,
|
...rounds,
|
||||||
...schedulingmodes,
|
|
||||||
...sessions,
|
...sessions,
|
||||||
...teams,
|
...teams,
|
||||||
...users
|
...users
|
||||||
|
|
38
src/lib/server/db/queries/competitions.ts
Normal file
38
src/lib/server/db/queries/competitions.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { db } from '..';
|
||||||
|
import { competitions } from '../schema/competitions';
|
||||||
|
import type { Round } from '../schema/rounds';
|
||||||
|
|
||||||
|
export async function getCompetitionWithAll(id: number) {
|
||||||
|
return await db.query.competitions.findFirst({
|
||||||
|
where: eq(competitions.id, id),
|
||||||
|
with: {
|
||||||
|
breakperiods: true,
|
||||||
|
fields: true,
|
||||||
|
rounds: {
|
||||||
|
with: {
|
||||||
|
matches: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
teams: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCompetitionsWithAll(skip: number = 0, take: number = 10) {
|
||||||
|
return await db.query.competitions.findMany({
|
||||||
|
orderBy: (competitions, { desc }) => [desc(competitions.start_date)],
|
||||||
|
with: {
|
||||||
|
breakperiods: true,
|
||||||
|
fields: true,
|
||||||
|
rounds: {
|
||||||
|
with: {
|
||||||
|
matches: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
teams: true
|
||||||
|
},
|
||||||
|
limit: take,
|
||||||
|
offset: skip
|
||||||
|
});
|
||||||
|
}
|
7
src/lib/server/db/queries/rounds.ts
Normal file
7
src/lib/server/db/queries/rounds.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { db } from '..';
|
||||||
|
import { rounds, type RoundInsert } from '../schema/rounds';
|
||||||
|
|
||||||
|
export async function insertRound(competitionId: number, round: RoundInsert) {
|
||||||
|
return await db.insert(rounds).values(round);
|
||||||
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
import * as t from 'drizzle-orm/pg-core';
|
import * as t from 'drizzle-orm/pg-core';
|
||||||
import { timestamps } from '../util';
|
import { timestamps } from '../util';
|
||||||
import { eq, relations } from 'drizzle-orm';
|
import { relations } from 'drizzle-orm';
|
||||||
import { users } from './users';
|
import { users } from './users';
|
||||||
import { breakperiods } from './breakperiods';
|
import { breakperiods } from './breakperiods';
|
||||||
import { fields } from './fields';
|
import { fields } from './fields';
|
||||||
import { teams } from './teams';
|
import { teams } from './teams';
|
||||||
import { rounds } from './rounds';
|
import { rounds } from './rounds';
|
||||||
import { db } from '..';
|
|
||||||
|
|
||||||
export const competitions = t.pgTable('competitions', {
|
export const competitions = t.pgTable('competitions', {
|
||||||
id: t.serial('id').primaryKey(),
|
id: t.serial('id').primaryKey(),
|
||||||
|
@ -30,39 +29,3 @@ export const competitionsRelations = relations(competitions, ({ one, many }) =>
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export type Competition = typeof competitions.$inferSelect;
|
export type Competition = typeof competitions.$inferSelect;
|
||||||
|
|
||||||
export async function getCompetitionWithAll(id: number) {
|
|
||||||
return await db.query.competitions.findFirst({
|
|
||||||
where: eq(competitions.id, id),
|
|
||||||
with: {
|
|
||||||
breakperiods: true,
|
|
||||||
fields: true,
|
|
||||||
rounds: {
|
|
||||||
with: {
|
|
||||||
matches: true,
|
|
||||||
schedulingMode: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
teams: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCompetitionsWithAll(skip: number = 0, take: number = 10) {
|
|
||||||
return await db.query.competitions.findMany({
|
|
||||||
orderBy: (competitions, { desc }) => [desc(competitions.start_date)],
|
|
||||||
with: {
|
|
||||||
breakperiods: true,
|
|
||||||
fields: true,
|
|
||||||
rounds: {
|
|
||||||
with: {
|
|
||||||
matches: true,
|
|
||||||
schedulingMode: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
teams: true
|
|
||||||
},
|
|
||||||
limit: take,
|
|
||||||
offset: skip
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,13 +2,15 @@ import { relations } from 'drizzle-orm';
|
||||||
import * as t from 'drizzle-orm/pg-core';
|
import * as t from 'drizzle-orm/pg-core';
|
||||||
import { matches } from './matches';
|
import { matches } from './matches';
|
||||||
import { competitions } from './competitions';
|
import { competitions } from './competitions';
|
||||||
import { schedulingModes } from './schedulingmodes';
|
import { enumToPgEnum, SchedulingMode } from '../../../../types';
|
||||||
|
|
||||||
|
export const schedulingModes = t.pgEnum('scheduling_modes', enumToPgEnum(SchedulingMode));
|
||||||
|
|
||||||
export const rounds = t.pgTable('rounds', {
|
export const rounds = t.pgTable('rounds', {
|
||||||
id: t.serial('id').primaryKey(),
|
id: t.serial('id').primaryKey(),
|
||||||
nb_matches: t.integer('nb_matches').notNull(),
|
nb_matches: t.integer('nb_matches').notNull(),
|
||||||
competition_id: t.integer('competition_id').notNull(),
|
competition_id: t.integer('competition_id').notNull(),
|
||||||
scheduling_mode: t.integer('scheduling_mode').notNull()
|
scheduling_mode: schedulingModes('scheduling_modes')
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roundsRelations = relations(rounds, ({ many, one }) => ({
|
export const roundsRelations = relations(rounds, ({ many, one }) => ({
|
||||||
|
@ -16,9 +18,9 @@ export const roundsRelations = relations(rounds, ({ many, one }) => ({
|
||||||
fields: [rounds.competition_id],
|
fields: [rounds.competition_id],
|
||||||
references: [competitions.id]
|
references: [competitions.id]
|
||||||
}),
|
}),
|
||||||
matches: many(matches),
|
matches: many(matches)
|
||||||
schedulingMode: one(schedulingModes, {
|
|
||||||
fields: [rounds.scheduling_mode],
|
|
||||||
references: [schedulingModes.id]
|
|
||||||
})
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export type Round = typeof rounds.$inferSelect;
|
||||||
|
|
||||||
|
export type RoundInsert = typeof rounds.$inferInsert;
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import * as t from 'drizzle-orm/pg-core';
|
|
||||||
import { timestamps } from '../util';
|
|
||||||
import { relations } from 'drizzle-orm';
|
|
||||||
import { rounds } from './rounds';
|
|
||||||
|
|
||||||
export const schedulingModes = t.pgTable('scheduling_modes', {
|
|
||||||
id: t.serial('id').primaryKey(),
|
|
||||||
name: t.text('name').notNull(),
|
|
||||||
description: t.text('description'),
|
|
||||||
algorithm: t.text('algorithm').notNull(),
|
|
||||||
config: t.jsonb('config').notNull(),
|
|
||||||
sequence_order: t.integer('sequence_order').notNull(),
|
|
||||||
status: t.text('status').notNull(),
|
|
||||||
round_id: t.integer('round_id').notNull(),
|
|
||||||
...timestamps
|
|
||||||
});
|
|
||||||
|
|
||||||
export const schedulingModesRelations = relations(schedulingModes, ({ one }) => ({
|
|
||||||
round: one(rounds, {
|
|
||||||
fields: [schedulingModes.round_id],
|
|
||||||
references: [rounds.id]
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
|
|
||||||
export type SchedulingMode = typeof schedulingModes.$inferSelect;
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as t from 'drizzle-orm/pg-core';
|
import * as t from 'drizzle-orm/pg-core';
|
||||||
|
|
||||||
export const timestamps = {
|
export const timestamps = {
|
||||||
updated_at: t.timestamp({ withTimezone: true, mode: 'date' }).default(new Date(Date.now())),
|
updated_at: t.timestamp({ withTimezone: true, mode: 'date' }).defaultNow(),
|
||||||
created_at: t.timestamp({ withTimezone: true, mode: 'date' }).defaultNow().notNull(),
|
created_at: t.timestamp({ withTimezone: true, mode: 'date' }).defaultNow().notNull(),
|
||||||
deleted_at: t.timestamp({ withTimezone: true, mode: 'date' }).default(new Date())
|
deleted_at: t.timestamp({ withTimezone: true, mode: 'date' })
|
||||||
};
|
};
|
||||||
|
|
||||||
export const binaryHash = t.customType<{ data: Buffer; driverData: Buffer }>({
|
export const binaryHash = t.customType<{ data: Buffer; driverData: Buffer }>({
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ModeWatcher } from 'mode-watcher';
|
|
||||||
import '../app.css';
|
|
||||||
import { setContext } from 'svelte';
|
|
||||||
import type { PageProps } from './$types';
|
|
||||||
import { Toaster } from '$lib/components/ui/sonner';
|
import { Toaster } from '$lib/components/ui/sonner';
|
||||||
|
import { ModeWatcher } from 'mode-watcher';
|
||||||
|
import { setContext, type Snippet } from 'svelte';
|
||||||
|
import '../app.css';
|
||||||
|
import type { LayoutData } from './$types';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
children,
|
data,
|
||||||
data
|
children
|
||||||
}: {
|
}: {
|
||||||
children: any;
|
data: LayoutData;
|
||||||
data: PageProps['data'];
|
children: Snippet;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
setContext('user', data.user);
|
setContext('user', data.user);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { getCompetitionsWithAll } from '$lib/server/db/schema/competitions';
|
import { getCompetitionsWithAll } from '@/lib/server/db/queries/competitions';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals }) => {
|
export const load: PageServerLoad = async ({ locals }) => {
|
||||||
|
|
|
@ -1,3 +1,41 @@
|
||||||
import type { RequestHandler } from '../../../competitions/[id]/$types';
|
import { insertRound } from '@/lib/server/db/queries/rounds';
|
||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
import type { RoundInsert } from '@/lib/server/db/schema/rounds';
|
||||||
|
import { SchedulingMode } from '@/types';
|
||||||
|
|
||||||
export const POST: RequestHandler = () => {};
|
export const POST: RequestHandler = async ({ request, params }) => {
|
||||||
|
// get body from request
|
||||||
|
// get competition id from url
|
||||||
|
const body = await request.json();
|
||||||
|
let competitionId;
|
||||||
|
try {
|
||||||
|
competitionId = Number.parseInt(params.id, 10);
|
||||||
|
} catch {
|
||||||
|
throw new Error('Invalid competition id');
|
||||||
|
}
|
||||||
|
if (competitionId === -1) {
|
||||||
|
throw new Error('Invalid competition id');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('body', body);
|
||||||
|
console.log('competitionId', competitionId);
|
||||||
|
|
||||||
|
let schedulingMode: SchedulingMode;
|
||||||
|
|
||||||
|
try {
|
||||||
|
schedulingMode = SchedulingMode[body.scheduling_mode as keyof typeof SchedulingMode];
|
||||||
|
} catch {
|
||||||
|
throw new Error('Invalid scheduling mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
// create round
|
||||||
|
const roundInsert: RoundInsert = {
|
||||||
|
nb_matches: 0,
|
||||||
|
competition_id: competitionId,
|
||||||
|
scheduling_mode: schedulingMode
|
||||||
|
};
|
||||||
|
|
||||||
|
const round = await insertRound(competitionId, roundInsert);
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(round));
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { db } from '$lib/server/db';
|
import { db } from '$lib/server/db';
|
||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
import { competitions, getCompetitionWithAll } from '$lib/server/db/schema/competitions';
|
import { competitions } from '$lib/server/db/schema/competitions';
|
||||||
import { error, redirect } from '@sveltejs/kit';
|
import { error, redirect } from '@sveltejs/kit';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { and } from 'drizzle-orm';
|
import { and } from 'drizzle-orm';
|
||||||
|
@ -8,6 +8,7 @@ import { teams } from '$lib/server/db/schema/teams';
|
||||||
import { superValidate } from 'sveltekit-superforms';
|
import { superValidate } from 'sveltekit-superforms';
|
||||||
import { zod } from 'sveltekit-superforms/adapters';
|
import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { formSchema } from '$lib/components/rounds/create-schema';
|
import { formSchema } from '$lib/components/rounds/create-schema';
|
||||||
|
import { getCompetitionWithAll } from '@/lib/server/db/queries/competitions';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ params, locals }) => {
|
export const load: PageServerLoad = async ({ params, locals }) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,29 +1,34 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { enhance } from '$app/forms';
|
||||||
|
import Round from '$lib/components/rounds/round.svelte';
|
||||||
import Badge from '$lib/components/ui/badge/badge.svelte';
|
import Badge from '$lib/components/ui/badge/badge.svelte';
|
||||||
import type { PageProps } from './$types';
|
|
||||||
import { CalendarDays, MapPin, Users, Trophy, Clock, Trash2Icon } from 'lucide-svelte';
|
|
||||||
import { formatDate } from '$lib/utils';
|
|
||||||
import Button from '$lib/components/ui/button/button.svelte';
|
import Button from '$lib/components/ui/button/button.svelte';
|
||||||
import Card from '$lib/components/ui/card/card.svelte';
|
|
||||||
import CardHeader from '$lib/components/ui/card/card-header.svelte';
|
|
||||||
import CardContent from '$lib/components/ui/card/card-content.svelte';
|
import CardContent from '$lib/components/ui/card/card-content.svelte';
|
||||||
import CardFooter from '$lib/components/ui/card/card-footer.svelte';
|
import CardFooter from '$lib/components/ui/card/card-footer.svelte';
|
||||||
|
import CardHeader from '$lib/components/ui/card/card-header.svelte';
|
||||||
|
import Card from '$lib/components/ui/card/card.svelte';
|
||||||
import Input from '$lib/components/ui/input/input.svelte';
|
import Input from '$lib/components/ui/input/input.svelte';
|
||||||
import { enhance } from '$app/forms';
|
import TabsContent from '$lib/components/ui/tabs/tabs-content.svelte';
|
||||||
import Textarea from '$lib/components/ui/textarea/textarea.svelte';
|
|
||||||
import Tabs from '$lib/components/ui/tabs/tabs.svelte';
|
|
||||||
import TabsList from '$lib/components/ui/tabs/tabs-list.svelte';
|
import TabsList from '$lib/components/ui/tabs/tabs-list.svelte';
|
||||||
import TabsTrigger from '$lib/components/ui/tabs/tabs-trigger.svelte';
|
import TabsTrigger from '$lib/components/ui/tabs/tabs-trigger.svelte';
|
||||||
import TabsContent from '$lib/components/ui/tabs/tabs-content.svelte';
|
import Tabs from '$lib/components/ui/tabs/tabs.svelte';
|
||||||
|
import Textarea from '$lib/components/ui/textarea/textarea.svelte';
|
||||||
|
import { formatDate } from '$lib/utils';
|
||||||
|
import { CalendarDays, Clock, MapPin, PlusIcon, Trash2Icon, Trophy, Users } from 'lucide-svelte';
|
||||||
import { Svelvet } from 'svelvet';
|
import { Svelvet } from 'svelvet';
|
||||||
import Rounds from '$lib/components/rounds/rounds.svelte';
|
import type { PageProps } from './$types';
|
||||||
|
import CreateRound from '@/lib/components/rounds/create-round.svelte';
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
|
let tabValue = $state('rounds');
|
||||||
|
|
||||||
let showInputTeam = $state(false);
|
let showInputTeam = $state(false);
|
||||||
let showInputDescription = $state(false);
|
let showInputDescription = $state(false);
|
||||||
let description = $state(data.competition.description);
|
let description = $state(data.competition.description);
|
||||||
|
|
||||||
|
let showAddRound = $state(false);
|
||||||
|
|
||||||
function clearAndCloseDescription() {
|
function clearAndCloseDescription() {
|
||||||
showInputDescription = false;
|
showInputDescription = false;
|
||||||
}
|
}
|
||||||
|
@ -59,11 +64,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Tabs value="rounds">
|
<Tabs bind:value={tabValue}>
|
||||||
|
<div class="flex justify-between">
|
||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger value="general">General</TabsTrigger>
|
<TabsTrigger value="general">General</TabsTrigger>
|
||||||
<TabsTrigger value="rounds">Rounds</TabsTrigger>
|
<TabsTrigger value="rounds">Rounds</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
<Button
|
||||||
|
hidden={tabValue !== 'rounds' ||
|
||||||
|
!data.competition.rounds ||
|
||||||
|
data.competition.rounds.length === 0}
|
||||||
|
onclick={() => (showAddRound = !showAddRound)}>Add Round<PlusIcon /></Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<TabsContent value="general">
|
<TabsContent value="general">
|
||||||
<div class="grid gap-6 md:grid-cols-3">
|
<div class="grid gap-6 md:grid-cols-3">
|
||||||
<Card class=" md:col-span-2">
|
<Card class=" md:col-span-2">
|
||||||
|
@ -166,8 +179,12 @@
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="rounds">
|
<TabsContent value="rounds">
|
||||||
{#if !data.competition.rounds || data.competition.rounds.length === 0}
|
{#if showAddRound || !data.competition.rounds || data.competition.rounds.length === 0}
|
||||||
<Rounds createForm={data.createForm} rounds={data.competition.rounds} />
|
<CreateRound competitionId={data.competition.id} />
|
||||||
|
{:else}
|
||||||
|
{#each data.competition.rounds as round (round.id)}
|
||||||
|
<Round {round} />
|
||||||
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
|
@ -20,3 +20,9 @@ export enum SchedulingMode {
|
||||||
round_robin = 'round_robin',
|
round_robin = 'round_robin',
|
||||||
double_round_robin = 'double_round_robin'
|
double_round_robin = 'double_round_robin'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function enumToPgEnum<T extends Record<string, any>>(
|
||||||
|
myEnum: T
|
||||||
|
): [T[keyof T], ...T[keyof T][]] {
|
||||||
|
return Object.values(myEnum).map((value: any) => `${value}`) as any;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue