Start adding filters, migrate types to zod, update readme
All checks were successful
build / build (push) Successful in 1m22s
All checks were successful
build / build (push) Successful in 1m22s
This commit is contained in:
58
README.md
58
README.md
@@ -1,59 +1,9 @@
|
|||||||
# IngeyEager
|
# IngeyEager
|
||||||
|
|
||||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.1.8.
|
A simple CSV viewer and analytics tool showcasing PrimeNG, Zod and DuckDB WASM. It all runs locally in the browser.
|
||||||
|
|
||||||
## Development server
|
## Build
|
||||||
|
|
||||||
To start a local development server, run:
|
`bun install`
|
||||||
|
|
||||||
```bash
|
`bun run build`
|
||||||
ng serve
|
|
||||||
```
|
|
||||||
|
|
||||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
|
||||||
|
|
||||||
## Code scaffolding
|
|
||||||
|
|
||||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ng generate component component-name
|
|
||||||
```
|
|
||||||
|
|
||||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ng generate --help
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
To build the project run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ng build
|
|
||||||
```
|
|
||||||
|
|
||||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ng test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running end-to-end tests
|
|
||||||
|
|
||||||
For end-to-end (e2e) testing, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ng e2e
|
|
||||||
```
|
|
||||||
|
|
||||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
|
||||||
|
|
||||||
## Additional Resources
|
|
||||||
|
|
||||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
|
||||||
|
|||||||
@@ -1,21 +1,39 @@
|
|||||||
import { Injectable, OnInit } from '@angular/core';
|
import { Injectable, OnInit } from '@angular/core';
|
||||||
import * as duckdb from '@duckdb/duckdb-wasm';
|
import * as duckdb from '@duckdb/duckdb-wasm';
|
||||||
import { connect } from 'rxjs';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export interface Column {
|
export const Column = z.object({
|
||||||
name: string;
|
name: z.string(),
|
||||||
type: string;
|
type: z.string(),
|
||||||
}
|
});
|
||||||
|
|
||||||
export interface SortColumn {
|
const SortColumn = z.object({
|
||||||
name: string;
|
name: z.string(),
|
||||||
sortType: 'asc' | 'desc';
|
sortType: z.enum(['asc', 'desc']),
|
||||||
}
|
});
|
||||||
|
|
||||||
export interface RowsResponse {
|
const Filter = z.object({
|
||||||
rows: any[];
|
value: z.string().array(),
|
||||||
totalRows: bigint;
|
operator: z.enum(['and', 'or']),
|
||||||
}
|
matchType: z.enum([
|
||||||
|
'startsWith',
|
||||||
|
'contains',
|
||||||
|
'notContains',
|
||||||
|
'endsWith',
|
||||||
|
'equals',
|
||||||
|
'notEquals',
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
const RowsResponse = z.object({
|
||||||
|
rows: z.any(),
|
||||||
|
totalRows: z.bigint(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Column = z.infer<typeof Column>;
|
||||||
|
export type SortColumn = z.infer<typeof SortColumn>;
|
||||||
|
export type Filter = z.infer<typeof Filter>;
|
||||||
|
export type RowsResponse = z.infer<typeof RowsResponse>;
|
||||||
|
|
||||||
const sanitisedFileName = (file: File) =>
|
const sanitisedFileName = (file: File) =>
|
||||||
file.name.toLowerCase().replaceAll("'", '').replaceAll(/\s*/g, '');
|
file.name.toLowerCase().replaceAll("'", '').replaceAll(/\s*/g, '');
|
||||||
@@ -104,7 +122,7 @@ export class DuckdbService {
|
|||||||
numRows: number,
|
numRows: number,
|
||||||
columns: Column[],
|
columns: Column[],
|
||||||
sorts: SortColumn[],
|
sorts: SortColumn[],
|
||||||
filters: unknown[],
|
filters: Filter[],
|
||||||
aggregations: unknown[],
|
aggregations: unknown[],
|
||||||
): Promise<RowsResponse> {
|
): Promise<RowsResponse> {
|
||||||
const conn = await this.db.connect();
|
const conn = await this.db.connect();
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
Component,
|
||||||
computed,
|
|
||||||
effect,
|
effect,
|
||||||
inject,
|
inject,
|
||||||
input,
|
input,
|
||||||
signal,
|
signal,
|
||||||
viewChild,
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Skeleton } from 'primeng/skeleton';
|
import { Skeleton } from 'primeng/skeleton';
|
||||||
import { Table, TableLazyLoadEvent, TableModule } from 'primeng/table';
|
import { TableLazyLoadEvent, TableModule } from 'primeng/table';
|
||||||
import { Column, DuckdbService } from '../duckdb.service';
|
import { Column, DuckdbService } from '../duckdb.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -34,6 +31,8 @@ import { Column, DuckdbService } from '../duckdb.service';
|
|||||||
[virtualScrollItemSize]="46"
|
[virtualScrollItemSize]="46"
|
||||||
[lazy]="true"
|
[lazy]="true"
|
||||||
(onLazyLoad)="onLazyLoad($event)"
|
(onLazyLoad)="onLazyLoad($event)"
|
||||||
|
[multiSortMeta]="[{ field: columns()[0].name, order: 1 }]"
|
||||||
|
stripedRows
|
||||||
>
|
>
|
||||||
<ng-template #header let-columns>
|
<ng-template #header let-columns>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -43,7 +42,9 @@ import { Column, DuckdbService } from '../duckdb.service';
|
|||||||
pResizableColumn
|
pResizableColumn
|
||||||
[pSortableColumn]="col.name"
|
[pSortableColumn]="col.name"
|
||||||
>
|
>
|
||||||
{{ col.name }} <p-sortIcon [field]="col.name" />
|
{{ col.name }}
|
||||||
|
<p-columnFilter type="text" [field]="col.name" display="menu" />
|
||||||
|
<p-sortIcon [field]="col.name" />
|
||||||
</th>
|
</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -116,7 +117,15 @@ export class FileViewerComponent {
|
|||||||
name: meta.field,
|
name: meta.field,
|
||||||
sortType: meta.order < 0 ? 'desc' : 'asc',
|
sortType: meta.order < 0 ? 'desc' : 'asc',
|
||||||
})) ?? [],
|
})) ?? [],
|
||||||
[],
|
Array.isArray(event.filters)
|
||||||
|
? Object.values(event.filters).map((filter) => ({
|
||||||
|
value: Array.isArray(filter.value)
|
||||||
|
? filter.value
|
||||||
|
: [filter.value],
|
||||||
|
matchType: filter.matchMode,
|
||||||
|
operator: filter.operator,
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
// First clear out existing data, don't want to risk loading entire file into memory
|
// First clear out existing data, don't want to risk loading entire file into memory
|
||||||
|
|||||||
Reference in New Issue
Block a user