Skip to content

Commit

Permalink
mereged with ui
Browse files Browse the repository at this point in the history
  • Loading branch information
Bereket Engida committed May 25, 2023
2 parents bce17dc + 990dc2b commit b486838
Show file tree
Hide file tree
Showing 30 changed files with 1,424 additions and 0 deletions.
73 changes: 73 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "@loglib/ui",
"version": "0.0.1",
"description": "",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"test": "vitest run",
"dev": "tsup --watch",
"build": "tsup",
"typecheck": "tsc --noEmit",
"lint": "eslint . --ext .ts"
},
"files": [
"dist",
"package.json",
"LICENSE",
"README.md"
],
"keywords": [],
"author": "",
"license": "ISC",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./dist/index.css": "./dist/index.css"
},
"devDependencies": {
"@types/react": "^18.2.0",
"next": "^13.4.1",
"react": "link:@formkit/auto-animate/react",
"react-dom": "^18.2.0"
},
"peerDependencies": {
"next": "^13.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"dependencies": {
"@loglib/core": "^0.0.1",
"@radix-ui/react-avatar": "^1.0.2",
"@radix-ui/react-dialog": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-label": "^2.0.1",
"@radix-ui/react-popover": "^1.0.5",
"@radix-ui/react-select": "^1.2.1",
"@radix-ui/react-separator": "^1.0.2",
"@radix-ui/react-slot": "^1.0.1",
"@radix-ui/react-tabs": "^1.0.3",
"@tanstack/react-table": "^8.9.1",
"@types/lodash": "^4.14.194",
"add": "^2.0.6",
"autoprefixer": "^10.4.14",
"avatar": "^0.1.0",
"class-variance-authority": "^0.6.0",
"clsx": "^1.2.1",
"cmdk": "^0.2.0",
"date-fns": "^2.30.0",
"framer-motion": "^10.12.12",
"lodash": "^4.17.21",
"lucide-react": "^0.220.0",
"pnpx": "^0.0.1",
"react-day-picker": "^8.7.1",
"recharts": "^2.6.2",
"shadcn-ui": "^0.1.3",
"smart-array-filter": "^4.0.2",
"tailwind-merge": "^1.12.0",
"tailwindcss": "^3.3.2",
"tailwindcss-animate": "^1.0.5"
}
}
176 changes: 176 additions & 0 deletions packages/ui/src/logic/filterBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
type StringOperator = "is" | "isNot" | "contains" | "notContains";
type NumberOperator = "lte" | "gte" | "lt" | "gt" | "is" | "isNot";
type DateOperator = "lte" | "gte" | "lt" | "gt" | "is" | "isNot";
type ArrayOperator = "contains" | "notContains";

type OperatorType<T> = T extends string ? StringOperator : T extends number ? NumberOperator : T extends Date ? DateOperator : T extends Array<any> ? ArrayOperator : never;


interface FilterFunction<T, S extends keyof T = keyof T> {
where: <U extends T[S]>(key: S, operator: OperatorType<U>, value: T[keyof T]) => FilterFunction<T>;
or: () => FilterFunction<T>;
and: () => FilterFunction<T>;
execute: () => T[];
sort: (key: keyof T, desc?: boolean) => FilterFunction<T>;
limit: (count: number) => FilterFunction<T>;
}
type Primitive = string | number | boolean | null | undefined | Date | Array<null | number | string>

const filterRoot = <T extends Record<S, Primitive>, S extends keyof T>(
data: T[],
key: S,
operator: OperatorType<T[S]>,
value: T[S]
): T[] => {
return data.filter((item) => {
switch (operator) {
case "is":
return item[key] === value;
case "isNot":
return item[key] !== value;
case "contains":
const a = item[key] as Array<string>;
return a.includes(value as string);
case "notContains":
const b = item[key] as Array<string>;
return !b.includes(value as string);
case "gt":
return item[key] > value;
case "lt":
return item[key] < value;
case "gte":
return item[key] >= value;
case "lte":
return item[key] <= value;
default:
return true;
}
});
};

const filter = <T extends Record<S, T[S]>, S extends keyof T>(data: T[]) => {
let andFilters: ((data: T[]) => T[])[] = [];
const orFilters: ((data: T[]) => T[])[] = [];
let limitCount: number | undefined;

const execute = (): T[] => {
let result = data;

if (andFilters.length > 0) {
for (const filterFn of andFilters) {
result = filterFn(result);
}
}

if (orFilters.length > 0) {
const orResults: T[][] = [];
for (const filterFn of orFilters) {
const filtered = filterFn(result);
if (filtered.length > 0) {
orResults.push(filtered);
}
}
if (orResults.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
result = [].concat(...orResults);
}
}

if (limitCount !== undefined && result.length > limitCount) {
result = result.slice(0, limitCount);
}
return result;
};

const limit = (count: number): FilterFunction<T> => {
limitCount = count;
return {
execute,
where,
or,
and,
sort,
limit,
};
};


const sort = (key: keyof T, desc?: boolean): FilterFunction<T> => {
//sort by key
const sorted = data.sort((a, b) => {
if (a[key] < b[key]) {
return -1;
}
if (a[key] > b[key]) {
return 1;
}
return 0;
});
if (desc) {
sorted.reverse();
}
return {
execute,
where,
or,
and,
sort,
limit
}
};

const where = <U extends T[S]>(key: S, operator: OperatorType<U>, value: T[S]): FilterFunction<T> => {
andFilters.push((data) => filterRoot(data, key, operator, value));
return {
where,
execute,
or,
and,
sort,
limit
};
};


const and = (...filters: { key: keyof T; operator: OperatorType<T[S]>; value: T[keyof T] }[]): FilterFunction<T> => {
orFilters.push((data) => {
let result = data;
for (const filter of filters) {
result = filterRoot(result, filter.key, filter.operator, filter.value);
}
return result;
});
return {
where,
execute,
or,
and,
sort,
limit
};
};

const or = (): FilterFunction<T> => {
const currentAndFilters = andFilters;
andFilters = [];
orFilters.push((data) => {
let result = data;
for (const filterFn of currentAndFilters) {
result = filterFn(result);
}
return result;
});
return {
where,
execute,
or,
and,
sort,
limit
};
};
return {
where
};
};
export { filter };
8 changes: 8 additions & 0 deletions packages/ui/src/logic/insights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

import { User } from "@loglib/core"
import { filter } from "./filter"


const unqiueUsers = (data: User[],) => {
filter(data).where("createdAt", "gte", new Date(new Date().getDate() - 1))
}
60 changes: 60 additions & 0 deletions packages/ui/src/react/components/BasicAnalytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"use client";
import {
Asterisk, MapPin,
MonitorSmartphone,
PanelTop
} from "lucide-react";
import { Card } from "./ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
import React from "react";
import { PagesComponent } from "./PagesComponent";
import { LocationsComponent } from "./LocationsComponent";
import { DeviceComponent } from "./DeviceComponent";
import { RefComponent } from "./RefComponent";
import { refs } from "../data/refs";
import { devices } from "../data/devices";
import { locations } from "../data/locations";
import { pageViews } from "../data/pageViews";

export function BasicAnalytics() {
return (
<Card className="col-span-3">
<Tabs defaultValue="pages">
<TabsList className="w-full space-x-2 justify-start">
<TabsTrigger value="pages" className=" space-x-2 ">
<PanelTop size={16} />
<p>Pages</p>
</TabsTrigger>
<TabsTrigger value="ref" className=" space-x-2">
<Asterisk size={16} />
<p>Referees</p>
</TabsTrigger>
<TabsTrigger value="device" className=" space-x-2">
<MonitorSmartphone size={16} />
<p>Devices</p>
</TabsTrigger>
<TabsTrigger
value="locations"
className=" space-x-2"
>
<MapPin size={16} />
<p>Locations</p>
</TabsTrigger>
</TabsList>

<TabsContent value="pages">
<PagesComponent pageViews={pageViews} />
</TabsContent>
<TabsContent value="ref">
<RefComponent refs={refs} />
</TabsContent>
<TabsContent value="device">
<DeviceComponent devices={devices} />
</TabsContent>
<TabsContent value="locations">
<LocationsComponent locations={locations} />
</TabsContent>
</Tabs>
</Card>
);
}
18 changes: 18 additions & 0 deletions packages/ui/src/react/components/DetailValue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";

const DetailValue = ({
keyName,
value,
}: {
keyName: string;
value: number | string;
}) => {
return (
<div className="p-2 flex justify-between sm:grid grid-cols-2 sm:grid-cols-3 ">
<dt className="font-semibold capitalize text-slate-600">{keyName}</dt>
<dd className="sm:col-span-2 text-slate-500">{value}</dd>
</div>
);
};

export default DetailValue;
43 changes: 43 additions & 0 deletions packages/ui/src/react/components/DeviceComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use client";
import { CardContent } from "./ui/card";
import React from "react";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "./ui/table";

export function DeviceComponent({ devices } : { devices : DevicesType[]} ) {
return (
<CardContent>
<Table>
<TableCaption>
Your devices and how many times they are visited {":)"}
</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Device</TableHead>
<TableHead className="text-right">Views</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{devices.map((device,i) => (
<TableRow key={i}>
<TableCell>{device.deviceName}</TableCell>
<TableCell className="text-right">{device.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
);
}

export type DevicesType = {
deviceName: string;
value: number;
};
Loading

0 comments on commit b486838

Please sign in to comment.