You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.8 KiB
Svelte

<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 { superForm } from 'sveltekit-superforms'
import { zod } from 'sveltekit-superforms/adapters'
import { formSchema, intervals } from './schema'
import { categories } 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'
let { data } = $props()
const apiEndpoint = 'https://api.bybit.com/v5/market/kline'
const form = superForm(data.form, {
resetForm: false,
SPA: true,
validators: zod(formSchema),
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())
if (form.data.category)
url.searchParams.set('category', form.data.category)
const response = await fetch(url)
const data = await response.json()
console.log(data.result.list)
let csvContent =
'data:text/csv;charset=utf-8,' +
'startTime,openPrice,highPrice,lowPrice,closePrice,volume,turnover\n' +
data.result.list
.map((data: string[]) => {
let newData = data
newData[0] = new Date(parseInt(newData[0]))
.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="category">
<Form.Control>
{#snippet children({ props })}
<Form.Label>Category</Form.Label>
<Select.Root
type="single"
bind:value={$formData.category}
name={props.name}>
<Select.Trigger {...props}>
{$formData.category ?? 'Select a category'}
</Select.Trigger>
<Select.Content>
{#each categories as category}
<Select.Item value={category} label={category} />
{/each}
</Select.Content>
</Select.Root>
{/snippet}
</Form.Control>
<Form.Description>Category</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>Category</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>
Your date of birth is used to calculator your age
</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>Limit</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>
Your date of birth is used to calculator your age
</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>Limit</Form.Description>
<Form.FieldErrors />
</Form.Field>
<Form.Button>Submit</Form.Button>
</form>