Skip to content

Commit 1cca1ca

Browse files
authored
Admin Queries (#49)
* Dummy commit for PR * Rework sort by idx
1 parent 1af9f21 commit 1cca1ca

File tree

7 files changed

+194
-12
lines changed

7 files changed

+194
-12
lines changed

components/kern-table/CellComponents.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ function ConfigCell({ config }) {
144144
</Tooltip>
145145
}
146146

147-
function EditDeleteOrgButtonCell({ button, clickEdit }) {
147+
function EditDeleteOrgButtonCell({ clickDelete, clickEdit }) {
148148
return <div className="flex flex-row gap-x-2 items-center">
149-
<div className="rounded-lg cursor-pointer" onClick={() => clickEdit(button)}>
149+
<div className="rounded-lg cursor-pointer" onClick={clickEdit}>
150150
<SVGIcon icon="IconFilePencil" size={32} strokeWidth={2} />
151151
</div>
152-
<KernButton text="Delete" onClick={button.delFunc} buttonColor="red" />
152+
<KernButton text="Delete" onClick={clickDelete} buttonColor="red" />
153153
</div>
154154
}
155155

components/kern-table/KernTable.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,45 @@ import { Tooltip } from "@nextui-org/react";
88
import MultilineTooltipAutoContent from "@/submodules/react-components/components/MultilineTooltipAuto";
99
import { NoTableEntriesYet } from "../NoTableEntriesYet";
1010
import { MemoIconCell, MemoIconEdit } from "../kern-icons/icons";
11+
import SortArrowsIdx from "./SortArrowsIdx";
1112

1213
export default function KernTable(props: KernTableProps) {
1314
const length = useMemo(() => props.headers?.length || 5, [props.headers?.length]);
15+
16+
17+
const onClickSortLookup = useMemo(() => {
18+
if (!props.headers) return undefined;
19+
const x = props.headers.map((header, idx) => {
20+
if (!header.hasSort) return undefined;
21+
if (!props.config) return undefined;
22+
if (props.config.sortKey && props.config.onClickSort) return () => props.config.onClickSort(header.id);
23+
if (props.config.sortKeyIdx && props.config.onClickSortIdx) return () => props.config.onClickSortIdx(idx);
24+
throw new Error("KernTable: No onClickSort or onClickSortIdx provided in config for sortable header: " + header.id);
25+
return undefined;
26+
})
27+
return x;
28+
}, [props.headers, props.config]);
29+
30+
const sortArrowLookup = useMemo(() => {
31+
if (!props.headers) return undefined;
32+
return props.headers.map((header, idx) => {
33+
if (!props.config || !(props.config.sortKeyIdx || props.config.sortKey)) return undefined;
34+
if (!header.hasSort) return undefined;
35+
if (props.config.sortKey) return <SortArrows sortKey={props.config.sortKey} property={header.id} />;
36+
if (props.config.sortKeyIdx) return <SortArrowsIdx sortKey={props.config.sortKeyIdx} idx={idx} />;
37+
return undefined;
38+
})
39+
}, [props.config, props.headers])
40+
1441
return (
1542
<table className={`min-w-full divide-y divide-gray-300 rounded-b-lg ${props.config && props.config?.addBorder ? 'border border-gray-300' : ''}`}>
1643
<thead className="bg-gray-50">
1744
<tr>
18-
{props.headers.map((header) => (
45+
{props.headers.map((header, idx) => (
1946
<th scope="col"
2047
className={`px-3 py-2 text-center text-xs font-medium uppercase tracking-wide text-gray-500 ${header.hasSort ? 'hover:text-gray-700 cursor-pointer' : ''}`}
2148
id={header.id} key={header.id}
22-
onClick={header.hasSort ? () => props.config.onClickSort(header.id) : undefined}
49+
onClick={onClickSortLookup[idx]}
2350
>
2451
{header.hasCheckboxes ? <>
2552
<input
@@ -36,7 +63,7 @@ export default function KernTable(props: KernTableProps) {
3663
color="invert"
3764
placement="top">Est. Precision</Tooltip>
3865
</div>}
39-
{header.hasSort && <SortArrows sortKey={props.config.sortKey} property={header.id} />}
66+
{sortArrowLookup[idx]}
4067
</div>}
4168
</th>))}
4269
</tr>
@@ -88,7 +115,7 @@ function ComponentMapper(cell: any) {
88115
case 'CancelTaskCell':
89116
return <CancelTaskCell {...cell} />;
90117
case 'IconCell':
91-
return <MemoIconCell {...cell} />;
118+
return <IconCell {...cell} />;
92119
case 'ConfigCell':
93120
return <ConfigCell {...cell} />;
94121
case 'EditDeleteOrgButtonCell':
@@ -131,7 +158,7 @@ function ComponentMapper(cell: any) {
131158
case 'text':
132159
return <span>{cell.value ?? <NotApplicableBadge />}</span>
133160
case 'number':
134-
return <span>{cell.value[1]}</span>
161+
return <span>{cell.value[1] ?? <NotApplicableBadge />}</span>
135162
case 'boolean':
136163
return <input type="checkbox" value={cell.value} checked={cell.checked} onClick={cell.valueChange ? cell.valueChange : undefined} readOnly />
137164
case 'dateInput':
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { SortArrowsPropsIdx } from '../../types/sort';
2+
import { MemoIconArrowDown, MemoIconArrowsSort, MemoIconArrowUp } from '../kern-icons/icons';
3+
4+
export default function SortArrowsIdx(props: SortArrowsPropsIdx) {
5+
6+
if (props.sortKey.idx != props.idx || (props.sortKey.idx == props.idx && props.sortKey.direction == 0))
7+
return <MemoIconArrowsSort className='text-gray-500 h-4 w-4' />
8+
9+
if (props.sortKey.idx == props.idx && props.sortKey.direction == 1)
10+
return <MemoIconArrowUp className='text-gray-500 h-4 w-4' />
11+
12+
return <MemoIconArrowDown className='text-gray-500 h-4 w-4' />
13+
}

helpers/kern-table-helper.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { dateAsUTCDate, parseUTC } from "@/submodules/javascript-functions/date-parser";
2+
3+
4+
//edit function can be added to any column automatically adding an icon to the column if it exists (e.g. org table admin-dashboard)
5+
export function extendEditFunction(column: any, editFunction: () => void) {
6+
return {
7+
...column,
8+
editFunction: editFunction
9+
}
10+
}
11+
12+
export function toTableColumnText(value: string) {
13+
return {
14+
type: "text",
15+
value: value
16+
}
17+
}
18+
19+
export function toTableColumnNumber(value: number) {
20+
return {
21+
type: "number",
22+
value: [value, value == null ? null : "" + value]
23+
}
24+
}
25+
26+
export function toTableColumnCheckbox(value: boolean, valueChange?: () => void) {
27+
return {
28+
type: "boolean",
29+
value: value,
30+
checked: value,
31+
valueChange: valueChange
32+
}
33+
}
34+
35+
export function toTableColumnComponent(component: string, sortValue: any, props?: any) {
36+
const base = {
37+
type: 'Component',
38+
component: component,
39+
value: sortValue
40+
}
41+
//sorting checks on value type & expects an array for date and number types
42+
if (base.value instanceof Date || typeof base.value == 'number') base.value = [base.value]
43+
if (!props) return base;
44+
45+
return {
46+
...base,
47+
...props
48+
}
49+
}
50+
51+
export function toTableColumnDate(value: string, onlyDate?: boolean) {
52+
return {
53+
type: 'date',
54+
value: [dateAsUTCDate(new Date(value || null)), parseUTC(value, onlyDate)]
55+
}
56+
}
57+
58+
export function toTableColumnInputDate(value: string, valueChange: (event) => void) {
59+
return {
60+
type: 'dateInput',
61+
value: [dateAsUTCDate(new Date(value || null)), value || ''],
62+
valueChange: valueChange
63+
}
64+
}
65+
66+
export function toTableColumnDropdown(value: string, options: any[], selectedOption?: (option: any) => void, disabled?: boolean) {
67+
return {
68+
type: 'dropdown',
69+
value: value,
70+
options: options,
71+
selectedOption: selectedOption,
72+
disabled: disabled
73+
}
74+
}

helpers/sort-functions.ts

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { SortDirection, SortKey } from "../types/sort";
1+
import { SortDirection, SortKey, SortKeyIdx } from "../types/sort";
22

3+
4+
//keeping some older methods to prevent breaking cognition
35
export function sortArrayByProperty(arr: any[], property: string, sortKey: SortKey, usersTable: boolean = false) {
46
if (!arr || arr.length == 0) return sortKey;
57
let order = nextSortDirection(property, sortKey);
@@ -67,14 +69,68 @@ export function getPropertyValue(obj: any, path: string) {
6769
return value;
6870
}
6971

72+
export function sortPreppedArrayByIdx(arr: any[][], idx: number, sortKey: SortKeyIdx) {
73+
if (!arr || arr.length == 0) return sortKey;
74+
75+
let dataType = arr[0][idx].type;
76+
if (dataType == 'Component' || dataType == 'dropdown') {
77+
if (!("value" in arr[0][idx])) throw new Error("No value found for idx: " + idx);
78+
let firstValue = arr[0][idx].value;
79+
if (Array.isArray(firstValue)) firstValue = firstValue[0];
80+
if (firstValue instanceof Date) dataType = 'date';
81+
else dataType = typeof firstValue;
82+
}
83+
sortKey = {
84+
idx: idx,
85+
dataType: dataType,
86+
direction: nextSortDirectionByIdx(idx, sortKey)
87+
};
88+
sortBySortKeyIdx(arr, sortKey);
89+
return sortKey;
90+
}
91+
92+
export function nextSortDirectionByIdx(idx: number, sortKey: SortKeyIdx): SortDirection {
93+
if (sortKey && sortKey.idx == idx) {
94+
if (sortKey.direction == SortDirection.ASC) return SortDirection.DESC;
95+
else if (sortKey.direction == SortDirection.DESC) return SortDirection.NO_SORT;
96+
else if (sortKey.direction == SortDirection.NO_SORT) return SortDirection.ASC;
97+
}
98+
return SortDirection.ASC;
99+
}
100+
101+
102+
export function sortBySortKeyIdx(arr: any[], sortKey: SortKeyIdx) {
103+
if (!sortKey) return;
104+
const order = sortKey.direction;
105+
switch (sortKey.dataType) {
106+
case 'string':
107+
arr.sort((a, b) => sortString(a[sortKey.idx].value, b[sortKey.idx].value, order));
108+
break;
109+
case 'number':
110+
arr.sort((a, b) => sortNumber(a[sortKey.idx].value[0], b[sortKey.idx].value[0], order));
111+
break;
112+
case 'date':
113+
case 'dateInput':
114+
arr.sort((a, b) => sortDate(a[sortKey.idx].value[0], b[sortKey.idx].value[0], order));
115+
break;
116+
case 'boolean':
117+
arr.sort((a, b) => sortBoolean(a[sortKey.idx].value, b[sortKey.idx].value, order));
118+
break;
119+
default:
120+
arr.sort((a, b) => sortString(a[sortKey.idx].value, b[sortKey.idx].value, order));
121+
break;
122+
}
123+
}
124+
125+
70126
export function sortString(a: string, b: string, order: SortDirection) {
71127
if (!a && a != '') return -1;
72128
if (!b && b != '') return 1;
73129
switch (order) {
74130
case SortDirection.ASC:
75-
return (a < b ? -1 : (a > b ? 1 : 0));
131+
return a.localeCompare(b);
76132
case SortDirection.DESC:
77-
return (b < a ? -1 : (b > a ? 1 : 0));
133+
return a.localeCompare(b) * -1;
78134
case SortDirection.NO_SORT:
79135
return 0;
80136
}

types/kern-table.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { SortKey } from "./sort";
1+
import { SortKey, SortKeyIdx } from "./sort";
22

33
export type KernTableProps = {
44
headers: { column: string, id: string, hasSort?: boolean, hasCheckboxes?: boolean, checked?: boolean, onChange?: any, tooltip?: string, wrapWhitespace?: boolean }[];
55
values?: any[];
66
config?: {
77
sortKey?: SortKey;
8+
sortKeyIdx?: SortKeyIdx;
89
onClickSort?: (property: string) => void;
10+
onClickSortIdx?: (idx: number) => void;
911
addBorder?: boolean;
1012
noEntriesText?: string;
1113
}

types/sort.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ export type SortArrowsProps = {
22
sortKey: SortKey;
33
property: string;
44
};
5+
export type SortArrowsPropsIdx = {
6+
sortKey: SortKeyIdx;
7+
idx: number;
8+
};
59

610

711
export type SortKey = {
@@ -10,6 +14,12 @@ export type SortKey = {
1014
direction: SortDirection;
1115
};
1216

17+
export type SortKeyIdx = {
18+
idx: number;
19+
dataType: string;
20+
direction: SortDirection;
21+
};
22+
1323
export enum SortDirection {
1424
ASC = 1,
1525
DESC = -1,

0 commit comments

Comments
 (0)