Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/npm_and_yarn/tracker/express-4.…
Browse files Browse the repository at this point in the history
…21.0
  • Loading branch information
cnkk authored Sep 12, 2024
2 parents 124a249 + 0a962e2 commit 358aa29
Show file tree
Hide file tree
Showing 55 changed files with 1,743 additions and 700 deletions.
1 change: 1 addition & 0 deletions .github/workflows/all-checks-pass.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: All checks pass

on:
pull_request:
merge_group:

jobs:
enforce-all-checks:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
push:
branches: [ master ]
merge_group:

jobs:
codespell:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
push:
branches: [master, stable]
merge_group:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches: [master, stable]
pull_request:
merge_group:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.
- Typescript support for `/assets`
- Testing framework for `/assets`
- Automatic HTTPS plausible/analytics#4491
- Make details views on dashboard sortable

### Removed
- Deprecate `ECTO_IPV6` and `ECTO_CH_IPV6` env vars in CE plausible/analytics#4245
Expand All @@ -39,6 +40,7 @@ All notable changes to this project will be documented in this file.
- Improve Google error messages in CE plausible/analytics#4485
- Better compress static assets in CE plausible/analytics#4476
- Return domain-less cookies in CE plausible/analytics#4482
- Internal stats API routes now return a JSON error over HTML in case of invalid access.

### Fixed

Expand Down
53 changes: 53 additions & 0 deletions assets/js/dashboard/components/search-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/** @format */

import React, { ChangeEventHandler, useCallback, useRef } from 'react'
import { isModifierPressed, Keybind } from '../keybinding'
import { useDebounce } from '../custom-hooks'
import classNames from 'classnames'

export const SearchInput = ({
onSearch,
className
}: {
className?: string
onSearch: (value: string) => void
}) => {
const searchBoxRef = useRef<HTMLInputElement>(null)

const onSearchInputChange: ChangeEventHandler<HTMLInputElement> = useCallback(
(event) => {
onSearch(event.target.value)
},
[onSearch]
)
const debouncedOnSearchInputChange = useDebounce(onSearchInputChange)

const blurSearchBox = useCallback((event: KeyboardEvent) => {
const searchBox = searchBoxRef.current
if (searchBox?.contains(event.target as HTMLElement)) {
searchBox.blur()
event.stopPropagation()
}
}, [])

return (
<>
<Keybind
keyboardKey="Escape"
type="keyup"
handler={blurSearchBox}
shouldIgnoreWhen={[isModifierPressed]}
/>
<input
ref={searchBoxRef}
type="text"
placeholder="Search"
className={classNames(
'shadow-sm dark:bg-gray-900 dark:text-gray-100 focus:ring-indigo-500 focus:border-indigo-500 block sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800 w-48',
className
)}
onChange={debouncedOnSearchInputChange}
/>
</>
)
}
44 changes: 44 additions & 0 deletions assets/js/dashboard/components/sort-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @format */

import React, { ReactNode } from 'react'
import { cycleSortDirection, SortDirection } from '../hooks/use-order-by'
import classNames from 'classnames'

export const SortButton = ({
children,
toggleSort,
sortDirection
}: {
children: ReactNode
toggleSort: () => void
sortDirection: SortDirection | null
}) => {
const next = cycleSortDirection(sortDirection)
return (
<button
onClick={toggleSort}
title={next.hint}
className={classNames('group', 'hover:underline', 'relative')}
>
{children}
<span
className={classNames(
'absolute',
'rounded inline-block h-4 w-4',
'ml-1',
{
[SortDirection.asc]: 'rotate-180',
[SortDirection.desc]: 'rotate-0'
}[sortDirection ?? next.direction],
!sortDirection && 'opacity-0',
!sortDirection && 'group-hover:opacity-100',
sortDirection &&
'group-hover:bg-gray-100 dark:group-hover:bg-gray-900',
'transition'
)}
>
</span>
</button>
)
}
151 changes: 151 additions & 0 deletions assets/js/dashboard/components/table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/** @format */

import classNames from 'classnames'
import React, { ReactNode } from 'react'
import { SortDirection } from '../hooks/use-order-by'
import { SortButton } from './sort-button'

export type ColumnConfiguraton<T extends Record<string, unknown>> = {
/** Unique column ID, used for sorting purposes and to get the value of the cell using rowItem[key] */
key: keyof T
/** Column title */
label: ReactNode
/** If defined, the column is considered sortable. @see SortButton */
onSort?: () => void
sortDirection?: SortDirection
/** CSS class string. @example "w-24 md:w-32" */
width: string
/** Aligns column content. */
align?: 'left' | 'right'
/**
* Function used to transform the value found at item[key] for the cell. Superseded by renderItem if present. @example 1120 => "1.1k"
*/
renderValue?: (value: unknown) => ReactNode
/** Function used to create richer cells */
renderItem?: (item: T) => ReactNode
}

export const TableHeaderCell = ({
children,
className,
align
}: {
children: ReactNode
className: string
align?: 'left' | 'right'
}) => {
return (
<th
className={classNames(
'p-2 text-xs font-bold text-gray-500 dark:text-gray-400 tracking-wide',
className
)}
align={align}
>
{children}
</th>
)
}

export const TableCell = ({
children,
className,
align
}: {
children: ReactNode
className: string
align?: 'left' | 'right'
}) => {
return (
<td className={classNames('p-2 font-medium', className)} align={align}>
{children}
</td>
)
}

export const ItemRow = <T extends Record<string, string | number | ReactNode>>({
rowIndex,
pageIndex,
item,
columns
}: {
rowIndex: number
pageIndex?: number
item: T
columns: ColumnConfiguraton<T>[]
}) => {
return (
<tr className="text-sm dark:text-gray-200">
{columns.map(({ key, width, align, renderValue, renderItem }) => (
<TableCell
key={`${(pageIndex ?? null) === null ? '' : `page_${pageIndex}_`}row_${rowIndex}_${String(key)}`}
className={width}
align={align}
>
{renderItem
? renderItem(item)
: renderValue
? renderValue(item[key])
: (item[key] ?? '')}
</TableCell>
))}
</tr>
)
}

export const Table = <T extends Record<string, string | number | ReactNode>>({
data,
columns
}: {
columns: ColumnConfiguraton<T>[]
data: T[] | { pages: T[][] }
}) => {
return (
<table className="w-max overflow-x-auto md:w-full table-striped table-fixed">
<thead>
<tr className="text-xs font-bold text-gray-500 dark:text-gray-400">
{columns.map((column) => (
<TableHeaderCell
key={`header_${String(column.key)}`}
className={classNames('p-2 tracking-wide', column.width)}
align={column.align}
>
{column.onSort ? (
<SortButton
toggleSort={column.onSort}
sortDirection={column.sortDirection ?? null}
>
{column.label}
</SortButton>
) : (
column.label
)}
</TableHeaderCell>
))}
</tr>
</thead>
<tbody>
{Array.isArray(data)
? data.map((item, rowIndex) => (
<ItemRow
item={item}
columns={columns}
rowIndex={rowIndex}
key={rowIndex}
/>
))
: data.pages.map((page, pageIndex) =>
page.map((item, rowIndex) => (
<ItemRow
item={item}
columns={columns}
rowIndex={rowIndex}
pageIndex={pageIndex}
key={`page_${pageIndex}_row_${rowIndex}`}
/>
))
)}
</tbody>
</table>
)
}
9 changes: 8 additions & 1 deletion assets/js/dashboard/datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import {
import classNames from 'classnames'
import { useQueryContext } from './query-context'
import { useSiteContext } from './site-context'
import { Keybind, KeybindHint, NavigateKeybind } from './keybinding'
import {
isModifierPressed,
isTyping,
Keybind,
KeybindHint,
NavigateKeybind
} from './keybinding'
import {
AppNavigationLink,
useAppNavigate
Expand Down Expand Up @@ -429,6 +435,7 @@ export default function QueryPeriodPicker() {
keyboardKey={keyboardKey}
type="keydown"
handler={onClick || closeMenu}
shouldIgnoreWhen={[isModifierPressed, isTyping]}
/>
) : (
<NavigateKeybind
Expand Down
Loading

0 comments on commit 358aa29

Please sign in to comment.