add bingx api
parent
a4d3c33512
commit
0f4630a534
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("p-6", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p bind:this={ref} class={cn("text-muted-foreground text-sm", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</p>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("flex items-center p-6 pt-0", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("flex flex-col space-y-1.5 p-6 pb-0", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
level = 3,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
||||||
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
role="heading"
|
||||||
|
aria-level={level}
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("bg-card text-card-foreground rounded-lg border shadow-sm", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import Root from "./card.svelte";
|
||||||
|
import Content from "./card-content.svelte";
|
||||||
|
import Description from "./card-description.svelte";
|
||||||
|
import Footer from "./card-footer.svelte";
|
||||||
|
import Header from "./card-header.svelte";
|
||||||
|
import Title from "./card-title.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Title,
|
||||||
|
//
|
||||||
|
Root as Card,
|
||||||
|
Content as CardContent,
|
||||||
|
Description as CardDescription,
|
||||||
|
Footer as CardFooter,
|
||||||
|
Header as CardHeader,
|
||||||
|
Title as CardTitle,
|
||||||
|
};
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import Scrollbar from "./scroll-area-scrollbar.svelte";
|
||||||
|
import Root from "./scroll-area.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Scrollbar,
|
||||||
|
//,
|
||||||
|
Root as ScrollArea,
|
||||||
|
Scrollbar as ScrollAreaScrollbar,
|
||||||
|
};
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { ScrollArea as ScrollAreaPrimitive, type WithoutChild } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
orientation = "vertical",
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChild<ScrollAreaPrimitive.ScrollbarProps> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ScrollAreaPrimitive.Scrollbar
|
||||||
|
bind:ref
|
||||||
|
{orientation}
|
||||||
|
class={cn(
|
||||||
|
"flex touch-none select-none transition-colors",
|
||||||
|
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-px",
|
||||||
|
orientation === "horizontal" && "h-2.5 w-full border-t border-t-transparent p-px",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
<ScrollAreaPrimitive.Thumb
|
||||||
|
class={cn("bg-border relative rounded-full", orientation === "vertical" && "flex-1")}
|
||||||
|
/>
|
||||||
|
</ScrollAreaPrimitive.Scrollbar>
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { ScrollArea as ScrollAreaPrimitive, type WithoutChild } from "bits-ui";
|
||||||
|
import { Scrollbar } from "./index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
orientation = "vertical",
|
||||||
|
scrollbarXClasses = "",
|
||||||
|
scrollbarYClasses = "",
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChild<ScrollAreaPrimitive.RootProps> & {
|
||||||
|
orientation?: "vertical" | "horizontal" | "both" | undefined;
|
||||||
|
scrollbarXClasses?: string | undefined;
|
||||||
|
scrollbarYClasses?: string | undefined;
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ScrollAreaPrimitive.Root bind:ref {...restProps} class={cn("relative overflow-hidden", className)}>
|
||||||
|
<ScrollAreaPrimitive.Viewport class="h-full w-full rounded-[inherit]">
|
||||||
|
{@render children?.()}
|
||||||
|
</ScrollAreaPrimitive.Viewport>
|
||||||
|
{#if orientation === "vertical" || orientation === "both"}
|
||||||
|
<Scrollbar orientation="vertical" class={scrollbarYClasses} />
|
||||||
|
{/if}
|
||||||
|
{#if orientation === "horizontal" || orientation === "both"}
|
||||||
|
<Scrollbar orientation="horizontal" class={scrollbarXClasses} />
|
||||||
|
{/if}
|
||||||
|
<ScrollAreaPrimitive.Corner />
|
||||||
|
</ScrollAreaPrimitive.Root>
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||||
|
import Content from "./tabs-content.svelte";
|
||||||
|
import List from "./tabs-list.svelte";
|
||||||
|
import Trigger from "./tabs-trigger.svelte";
|
||||||
|
|
||||||
|
const Root = TabsPrimitive.Root;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
List,
|
||||||
|
Trigger,
|
||||||
|
//
|
||||||
|
Root as Tabs,
|
||||||
|
Content as TabsContent,
|
||||||
|
List as TabsList,
|
||||||
|
Trigger as TabsTrigger,
|
||||||
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: TabsPrimitive.ContentProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"ring-offset-background focus-visible:ring-ring mt-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: TabsPrimitive.ListProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TabsPrimitive.List
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"bg-muted text-muted-foreground inline-flex h-10 items-center justify-center rounded-md p-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Tabs as TabsPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: TabsPrimitive.TriggerProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
@ -0,0 +1,289 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Form from '$lib/components/ui/form'
|
||||||
|
import * as Select from '$lib/components/ui/select'
|
||||||
|
import { Input } from '$lib/components/ui/input'
|
||||||
|
import { defaults, superForm } from 'sveltekit-superforms'
|
||||||
|
import { zod } from 'sveltekit-superforms/adapters'
|
||||||
|
import { bingxAPISchema, intervals } from './schema'
|
||||||
|
import { CalendarIcon } from 'lucide-svelte'
|
||||||
|
import { Calendar } from '$lib/components/ui/calendar/index.js'
|
||||||
|
import * as Popover from '$lib/components/ui/popover/index.js'
|
||||||
|
import {
|
||||||
|
CalendarDate,
|
||||||
|
DateFormatter,
|
||||||
|
type DateValue,
|
||||||
|
getLocalTimeZone,
|
||||||
|
parseDate,
|
||||||
|
today,
|
||||||
|
} from '@internationalized/date'
|
||||||
|
import { cn } from '$lib/utils.js'
|
||||||
|
|
||||||
|
import { buttonVariants } from '$lib/components/ui/button'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const apiEndpoint =
|
||||||
|
'https://open-api.bingx.io/openApi/swap/v3/quote/klines'
|
||||||
|
|
||||||
|
const form = superForm(
|
||||||
|
defaults(zod(bingxAPISchema), {
|
||||||
|
defaults: {
|
||||||
|
limit: 500,
|
||||||
|
symbol: 'BTC-USDT',
|
||||||
|
interval: '1h',
|
||||||
|
start: '2024-11-15',
|
||||||
|
startTime: '00:00',
|
||||||
|
end: '2024-11-15',
|
||||||
|
endTime: '00:00',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
resetForm: false,
|
||||||
|
SPA: true,
|
||||||
|
validators: zod(bingxAPISchema),
|
||||||
|
onUpdated: async ({ form }) => {
|
||||||
|
if (!form.valid) return
|
||||||
|
|
||||||
|
console.log(form.data)
|
||||||
|
// Form validation
|
||||||
|
const url = new URL(apiEndpoint)
|
||||||
|
url.searchParams.set('symbol', form.data.symbol)
|
||||||
|
url.searchParams.set('interval', form.data.interval)
|
||||||
|
|
||||||
|
const start = new Date(
|
||||||
|
form.data.start + ' ' + form.data.startTime + ':00',
|
||||||
|
)
|
||||||
|
url.searchParams.set('start', start.getTime().toString())
|
||||||
|
|
||||||
|
const end = new Date(
|
||||||
|
form.data.end + ' ' + form.data.endTime + ':00',
|
||||||
|
)
|
||||||
|
|
||||||
|
url.searchParams.set('end', end.getTime().toString())
|
||||||
|
|
||||||
|
if (form.data.limit)
|
||||||
|
url.searchParams.set('limit', form.data.limit.toString())
|
||||||
|
|
||||||
|
url.searchParams.set(
|
||||||
|
'timestamp',
|
||||||
|
new Date().getTime().toString(),
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await fetch('/api/bingx', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ url }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
const list = data.data as {
|
||||||
|
open: string
|
||||||
|
close: string
|
||||||
|
high: string
|
||||||
|
low: string
|
||||||
|
volume: string
|
||||||
|
time: number
|
||||||
|
}[]
|
||||||
|
|
||||||
|
let csvContent =
|
||||||
|
'data:text/csv;charset=utf-8,' +
|
||||||
|
'openPrice,closePrice,highPrice,lowPrice,volume,startTime\n' +
|
||||||
|
list
|
||||||
|
.map((data) => {
|
||||||
|
let newData = []
|
||||||
|
newData.push(data.open)
|
||||||
|
newData.push(data.close)
|
||||||
|
newData.push(data.high)
|
||||||
|
newData.push(data.low)
|
||||||
|
newData.push(data.volume)
|
||||||
|
newData.push(
|
||||||
|
new Date(data.time)
|
||||||
|
.toLocaleString()
|
||||||
|
.replace(',', ' |'),
|
||||||
|
)
|
||||||
|
return newData
|
||||||
|
})
|
||||||
|
.map((e: any) => e.join(','))
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
|
var encodedUri = encodeURI(csvContent)
|
||||||
|
var link = document.createElement('a')
|
||||||
|
link.setAttribute('href', encodedUri)
|
||||||
|
link.setAttribute('download', 'data.csv')
|
||||||
|
document.body.appendChild(link)
|
||||||
|
|
||||||
|
link.click()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const { form: formData, enhance } = form
|
||||||
|
|
||||||
|
const df = new DateFormatter('en-US', {
|
||||||
|
dateStyle: 'long',
|
||||||
|
})
|
||||||
|
|
||||||
|
let start = $state<DateValue | undefined>()
|
||||||
|
let end = $state<DateValue | undefined>()
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
start = $formData.start ? parseDate($formData.start) : undefined
|
||||||
|
end = $formData.end ? parseDate($formData.end) : undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
let placeholder = $state<DateValue>(today(getLocalTimeZone()))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form method="POST" use:enhance>
|
||||||
|
<Form.Field {form} name="symbol">
|
||||||
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
|
<Form.Label>Symbol</Form.Label>
|
||||||
|
<Input {...props} bind:value={$formData.symbol} />
|
||||||
|
{/snippet}
|
||||||
|
</Form.Control>
|
||||||
|
<Form.Description>Symbol</Form.Description>
|
||||||
|
<Form.FieldErrors />
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field {form} name="limit">
|
||||||
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
|
<Form.Label>Limit</Form.Label>
|
||||||
|
<Input
|
||||||
|
{...props}
|
||||||
|
bind:value={$formData.limit}
|
||||||
|
type="number" />
|
||||||
|
{/snippet}
|
||||||
|
</Form.Control>
|
||||||
|
<Form.Description>Limit</Form.Description>
|
||||||
|
<Form.FieldErrors />
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field {form} name="interval">
|
||||||
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
|
<Form.Label>Interval</Form.Label>
|
||||||
|
<Select.Root
|
||||||
|
type="single"
|
||||||
|
bind:value={$formData.interval}
|
||||||
|
name={props.name}>
|
||||||
|
<Select.Trigger {...props}>
|
||||||
|
{$formData.interval ?? 'Select a interval'}
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
{#each intervals as interval}
|
||||||
|
<Select.Item value={interval} label={interval} />
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
{/snippet}
|
||||||
|
</Form.Control>
|
||||||
|
<Form.Description>Interval</Form.Description>
|
||||||
|
<Form.FieldErrors />
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field {form} name="start" class="flex flex-col">
|
||||||
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
|
<Form.Label>Start Date</Form.Label>
|
||||||
|
<Popover.Root>
|
||||||
|
<Popover.Trigger
|
||||||
|
{...props}
|
||||||
|
class={cn(
|
||||||
|
buttonVariants({ variant: 'outline' }),
|
||||||
|
'w-[280px] justify-start pl-4 text-left font-normal',
|
||||||
|
!start && 'text-muted-foreground',
|
||||||
|
)}>
|
||||||
|
{start
|
||||||
|
? df.format(start.toDate(getLocalTimeZone()))
|
||||||
|
: 'Pick a date'}
|
||||||
|
<CalendarIcon class="ml-auto size-4 opacity-50" />
|
||||||
|
</Popover.Trigger>
|
||||||
|
<Popover.Content class="w-auto p-0" side="top">
|
||||||
|
<Calendar
|
||||||
|
type="single"
|
||||||
|
value={start as DateValue}
|
||||||
|
bind:placeholder
|
||||||
|
minValue={new CalendarDate(1900, 1, 1)}
|
||||||
|
maxValue={today(getLocalTimeZone())}
|
||||||
|
calendarLabel="Date of birth"
|
||||||
|
onValueChange={(v) => {
|
||||||
|
if (v) {
|
||||||
|
$formData.start = v.toString()
|
||||||
|
} else {
|
||||||
|
$formData.start = ''
|
||||||
|
}
|
||||||
|
}} />
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
|
<Form.Description>Start Date</Form.Description>
|
||||||
|
<Form.FieldErrors />
|
||||||
|
<input hidden value={$formData.start} name={props.name} />
|
||||||
|
{/snippet}
|
||||||
|
</Form.Control>
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field {form} name="startTime">
|
||||||
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
|
<Form.Label>Start Time</Form.Label>
|
||||||
|
<Input
|
||||||
|
{...props}
|
||||||
|
bind:value={$formData.startTime}
|
||||||
|
type="time" />
|
||||||
|
{/snippet}
|
||||||
|
</Form.Control>
|
||||||
|
<Form.Description>Start Time</Form.Description>
|
||||||
|
<Form.FieldErrors />
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field {form} name="end" class="flex flex-col">
|
||||||
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
|
<Form.Label>End Date</Form.Label>
|
||||||
|
<Popover.Root>
|
||||||
|
<Popover.Trigger
|
||||||
|
{...props}
|
||||||
|
class={cn(
|
||||||
|
buttonVariants({ variant: 'outline' }),
|
||||||
|
'w-[280px] justify-start pl-4 text-left font-normal',
|
||||||
|
!end && 'text-muted-foreground',
|
||||||
|
)}>
|
||||||
|
{end
|
||||||
|
? df.format(end.toDate(getLocalTimeZone()))
|
||||||
|
: 'Pick a date'}
|
||||||
|
<CalendarIcon class="ml-auto size-4 opacity-50" />
|
||||||
|
</Popover.Trigger>
|
||||||
|
<Popover.Content class="w-auto p-0" side="top">
|
||||||
|
<Calendar
|
||||||
|
type="single"
|
||||||
|
value={end as DateValue}
|
||||||
|
bind:placeholder
|
||||||
|
minValue={new CalendarDate(1900, 1, 1)}
|
||||||
|
maxValue={today(getLocalTimeZone())}
|
||||||
|
calendarLabel="Date of birth"
|
||||||
|
onValueChange={(v) => {
|
||||||
|
if (v) {
|
||||||
|
$formData.end = v.toString()
|
||||||
|
} else {
|
||||||
|
$formData.end = ''
|
||||||
|
}
|
||||||
|
}} />
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
|
<Form.Description>End Date</Form.Description>
|
||||||
|
<Form.FieldErrors />
|
||||||
|
<input hidden value={$formData.end} name={props.name} />
|
||||||
|
{/snippet}
|
||||||
|
</Form.Control>
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field {form} name="endTime">
|
||||||
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
|
<Form.Label>End Time</Form.Label>
|
||||||
|
<Input
|
||||||
|
{...props}
|
||||||
|
bind:value={$formData.endTime}
|
||||||
|
type="time" />
|
||||||
|
{/snippet}
|
||||||
|
</Form.Control>
|
||||||
|
<Form.Description>End Time</Form.Description>
|
||||||
|
<Form.FieldErrors />
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Button>Submit</Form.Button>
|
||||||
|
</form>
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export type Interval =
|
||||||
|
| '1m'
|
||||||
|
| '3m'
|
||||||
|
| '5m'
|
||||||
|
| '15m'
|
||||||
|
| '30m'
|
||||||
|
| '1h'
|
||||||
|
| '2h'
|
||||||
|
| '4h'
|
||||||
|
| '6h'
|
||||||
|
| '8h'
|
||||||
|
| '12h'
|
||||||
|
| '1d'
|
||||||
|
| '3d'
|
||||||
|
| '1w'
|
||||||
|
| '1M'
|
||||||
|
|
||||||
|
export const intervals: Interval[] = [
|
||||||
|
'1m',
|
||||||
|
'3m',
|
||||||
|
'5m',
|
||||||
|
'15m',
|
||||||
|
'30m',
|
||||||
|
'1h',
|
||||||
|
'2h',
|
||||||
|
'4h',
|
||||||
|
'6h',
|
||||||
|
'8h',
|
||||||
|
'12h',
|
||||||
|
'1d',
|
||||||
|
'3d',
|
||||||
|
'1w',
|
||||||
|
'1M',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const bingxAPISchema = z.object({
|
||||||
|
symbol: z.string(),
|
||||||
|
interval: z.custom<Interval>(),
|
||||||
|
start: z.string().min(1),
|
||||||
|
startTime: z.string().min(1),
|
||||||
|
end: z.string().min(1),
|
||||||
|
endTime: z.string().min(1),
|
||||||
|
limit: z.number().int().min(1).max(1000).optional(),
|
||||||
|
})
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { json } from '@sveltejs/kit'
|
||||||
|
import type { RequestHandler } from '../$types'
|
||||||
|
|
||||||
|
export const POST: RequestHandler = async ({ request }) => {
|
||||||
|
const body = await request.json()
|
||||||
|
|
||||||
|
const response = await fetch(body.url)
|
||||||
|
|
||||||
|
return json(await response.json())
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue