Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions web/components/Filtre.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@ import { navigate, url } from '../lib/router.tsx'

type FilterRow = { idx: number; key: string; op: string; value: string }

const filterOperators = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like'] as const
const filterOperators = [
'eq',
'neq',
'gt',
'gte',
'lt',
'lte',
'like',
'ilike',
] as const

export function parseFilters(prefix: string): FilterRow[] {
const data = url.getAll(`f${prefix}`)
Expand All @@ -28,7 +37,10 @@ function setFilters(prefix: string, rows: FilterRow[]) {
const v = rows[i]
next.push([v.key, v.op, v.value].join(','))
}
navigate({ params: { [`f${prefix}`]: next }, replace: true })
navigate({
params: { [`f${prefix}`]: next, [`${prefix}page`]: null },
replace: true,
})
}

function addFilter(prefix: string) {
Expand Down Expand Up @@ -115,12 +127,17 @@ export const FilterMenu = (
const rows = parseFilters(prefix)

return (
<details class='dropdown dropdown-end'>
<summary class='btn btn-outline btn-sm'>
<div class='dropdown dropdown-end'>
<button
type='button'
class='btn btn-outline btn-sm'
popovertarget='popover-1'
style='anchor-name:--anchor-1'
>
<Filter class='h-4 w-4' />
Filters
</summary>
<div class='dropdown-content z-10 w-110 mt-2'>
</button>
<div class='dropdown-content w-110 mt-2'>
<div class='bg-base-100 rounded-box shadow border border-base-300 p-3 space-y-3'>
<div class='space-y-2 max-h-72 overflow-y-auto pr-1'>
{rows.map((r) => (
Expand Down Expand Up @@ -185,7 +202,7 @@ export const FilterMenu = (
</button>
</div>
</div>
</details>
</div>
)
}

Expand All @@ -196,12 +213,17 @@ export const SortMenu = ({ tag, sortKeyOptions }: {
const prefix = tag === 'tables' ? 't' : 'l'
const rows = parseSort(prefix)
return (
<details class='dropdown dropdown-end'>
<summary class='btn btn-outline btn-sm'>
<div class='dropdown dropdown-end'>
<button
type='button'
class='btn btn-outline btn-sm'
popovertarget='popover-1'
style='anchor-name:--anchor-1'
>
<ArrowUpDown class='h-4 w-4' />
Sort
</summary>
<div class='dropdown-content z-10 w-80 mt-2'>
</button>
<div class='dropdown-content w-80 mt-2'>
<div class='bg-base-100 rounded-box shadow border border-base-300 p-3 space-y-3'>
<div class='space-y-2 max-h-60 overflow-y-auto pr-1'>
{rows.map((r) => (
Expand Down Expand Up @@ -253,6 +275,6 @@ export const SortMenu = ({ tag, sortKeyOptions }: {
</button>
</div>
</div>
</details>
</div>
)
}
112 changes: 112 additions & 0 deletions web/components/QueryHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { ChevronRight, Clock, Play, Search, Trash2 } from 'lucide-preact'
import { A, navigate, url } from '../lib/router.tsx'
import { onRun, queriesHistory } from '../pages/DeploymentPage.tsx'

export type QueryHistoryItem = {
query: string
timestamp: string
columns?: number
rows?: number
}

const deleteQuery = (hash: string) => {
const updatedHistory = { ...queriesHistory.value }
delete updatedHistory[hash]
queriesHistory.value = updatedHistory
}

export const QueryHistory = () => {
const filteredHistory = Object.entries(queriesHistory.value).sort((a, b) =>
new Date(b[1].timestamp).getTime() - new Date(a[1].timestamp).getTime()
).filter(([_, item]) => {
const qh = url.params.qh?.toLowerCase() || ''
return item.query.toLowerCase().includes(qh)
})

return (
<div class='flex flex-col h-full'>
<div class='p-4 border-b border-base-300'>
<h2 class='text-lg font-semibold'>Query History</h2>
<div class='flex items-center justify-between mt-4 gap-2'>
<label class='input input-sm min-w-0 w-full sm:w-64'>
<Search class='opacity-50' />
<input
type='search'
class='grow'
placeholder='Search'
value={url.params.qh || ''}
onInput={(e) => {
const value = (e.target as HTMLInputElement).value
navigate({ params: { qh: value || null }, replace: true })
}}
/>
</label>
<button
type='button'
class='btn btn-xs btn-ghost text-error'
title='Delete All from history'
disabled={Object.keys(queriesHistory.value).length === 0}
onClick={() => queriesHistory.value = {}}
>
<Trash2 class='w-4 h-4' />
Clear All
</button>
</div>
</div>
<div class='flex-1 overflow-auto'>
{filteredHistory.map(([hash, item]) => (
<div
key={hash}
class='p-4 border-b border-base-300 hover:bg-base-200'
>
<div class='flex items-center justify-between'>
<div class='flex-1 min-w-0'>
<div class='text-xs text-base-content/60 flex items-center gap-2'>
<Clock class='w-3 h-3' />
{new Date(item.timestamp).toLocaleString()}
</div>
<p class='font-mono text-sm truncate mt-1' title={item.query}>
{item.query}
</p>
<div class='text-xs text-base-content/60 mt-1'>
{item.columns} columns, {item.rows} rows
</div>
</div>
<div class='flex items-center gap-2 shrink-0 ml-4'>
<A
params={{ q: item.query, tab: 'queries' }}
class='btn btn-xs btn-ghost'
title='Run query'
onClick={() => onRun(item.query)}
>
<Play class='w-4 h-4' />
</A>
<A
class='btn btn-xs btn-ghost'
title='Insert into editor'
params={{ q: item.query, tab: 'queries' }}
>
<ChevronRight class='w-4 h-4' />
</A>
<button
type='button'
class='btn btn-xs btn-ghost text-error'
title='Delete from history'
disabled={!deleteQuery}
onClick={() => deleteQuery(hash)}
>
<Trash2 class='w-4 h-4' />
</button>
</div>
</div>
</div>
))}
{filteredHistory.length === 0 && (
<div class='p-4 text-center text-base-content/60'>
No queries found.
</div>
)}
</div>
</div>
)
}
17 changes: 8 additions & 9 deletions web/components/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ export function Sidebar(
title?: string
},
) {
const { sb } = url.params
const sb = url.params.sb
return (
<div class='drawer-side'>
<label htmlFor='drawer-toggle' class='drawer-overlay'></label>
<div
class={`${
sb ? 'w-16' : 'w-64'
sb ? 'w-64' : 'w-16'
} min-h-94/100 bg-base-100 border-r border-base-300 flex flex-col transition-all duration-300`}
>
<div class='p-4 border-b border-base-300'>
<div class='flex items-center justify-between'>
{!sb && (
{sb && (
<span class='text-sm font-medium rounded bg-base-200 w-7/10 py-1 px-2 text-center text-base-content/60'>
{title || 'Project Name'}
</span>
Expand All @@ -42,8 +41,8 @@ export function Sidebar(
class='p-2 hover:bg-base-200 rounded'
>
{sb
? <ChevronsRight class='h-4 w-4 text-base-content/60' />
: <ChevronsLeft class='h-4 w-4 text-base-content/60' />}
? <ChevronsLeft class='h-4 w-4 text-base-content/60' />
: <ChevronsRight class='h-4 w-4 text-base-content/60' />}
</A>
</div>
</div>
Expand All @@ -54,12 +53,12 @@ export function Sidebar(
<li key={slug}>
<A
class={`${sbi === slug ? 'bg-base-200' : ''}`}
data-tip={sb ? item.label : undefined}
data-tip={sb ? undefined : item.label}
params={{ sbi: slug }}
replace
>
<item.icon class='h-4 w-4' />
{!sb && item.label}
{sb && item.label}
</A>
</li>
))}
Expand All @@ -77,7 +76,7 @@ export function Sidebar(
}`}
>
<Settings class='h-4 w-4' />
{!sb && 'Settings'}
{sb && 'Settings'}
</A>
</div>
</div>
Expand Down
Loading
Loading