Skip to content

Commit

Permalink
data plotting of table
Browse files Browse the repository at this point in the history
  • Loading branch information
aatbip committed Jan 31, 2024
1 parent e5dab64 commit 0bf24d9
Show file tree
Hide file tree
Showing 14 changed files with 258 additions and 126 deletions.
17 changes: 13 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
COPILOT_API_KEY=""
POSTGRES_PRISMA_URL=""
POSTGRES_URL_NON_POOLING=""
COPILOT_API_KEY="b4fa0b79be594f91a2c72c63216d45ec.6463d9dc556351b6"

# Vercel Database
POSTGRES_PRISMA_URL="postgres://default:2tCAVY7Npmyc@ep-morning-mountain-74331247-pooler.us-east-1.postgres.vercel-storage.com/verceldb?pgbouncer=true&connect_timeout=15"
POSTGRES_URL_NON_POOLING="postgres://default:2tCAVY7Npmyc@ep-morning-mountain-74331247.us-east-1.postgres.vercel-storage.com/verceldb"

# Local Database
; POSTGRES_PRISMA_URL="postgresql://db:[email protected]:59004/db?schema=public"
; POSTGRES_URL_NON_POOLING="postgresql://db:[email protected]:59004/db?schema=public"

COPILOT_API_URL="https://api-beta.copilot.com"
COPILOT_ENV="local"
COPILOT_API_URL=""
VERCEL_URL="localhost:3000"
VERCEL_ENV="development"
20 changes: 11 additions & 9 deletions src/app/api/client-profile-updates/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,19 @@ export async function GET(request: NextRequest) {
const client = clientLookup[update.clientId];
const company = companyLookup[update.companyId];

const customFields = portalCustomFields.data?.map((portalCustomField) => {
let parsedClientProfileUpdate: ParsedClientProfileUpdatesResponse = {
id: update.id,
client: getClientDetails(client),
company: getCompanyDetails(company),
lastUpdated: update.createdAt,
};

portalCustomFields.data?.forEach((portalCustomField) => {
const value = update.customFields[portalCustomField.key] ?? null;
const options = getSelectedOptions(portalCustomField, value);

return {
// @ts-ignore
parsedClientProfileUpdate[portalCustomField.name] = {
name: portalCustomField.name,
type: portalCustomField.type,
key: portalCustomField.key,
Expand All @@ -83,13 +91,7 @@ export async function GET(request: NextRequest) {
};
});

return {
id: update.id,
client: getClientDetails(client),
company: getCompanyDetails(company),
lastUpdated: update.createdAt,
customFields,
};
return parsedClientProfileUpdate;
});

return NextResponse.json(parsedClientProfileUpdates);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class ClientProfileUpdatesService {
private prismaClient: PrismaClient = DBClient.getInstance();

async save(requestData: ClientProfileUpdates): Promise<void> {
await this.prismaClient.clientProfileUpdates.create({
await this.prismaClient.clientProtileUpdates.create({
data: {
clientId: requestData.clientId,
companyId: requestData.companyId,
Expand Down Expand Up @@ -52,7 +52,8 @@ export class ClientProfileUpdatesService {
WHERE "clientId" = ${clientId}::uuid
AND "createdAt" <= ${lastUpdated}
AND "changedFields" ->> ${customFieldKey} IS NOT NULL
ORDER BY "createdAt" DESC;
ORDER BY "createdAt" DESC
LIMIT 5;
`;
}
}
32 changes: 29 additions & 3 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import { Box, Stack, Typography } from '@mui/material';
import { Stack } from '@mui/material';
import { Sidebar } from './views/Sidebar';
import MainSection from './views/MainSection';
import { SidebarDecider } from '@/hoc/SidebarDecider';
import { apiUrl } from '@/config';
import { ParsedClientProfileUpdatesResponse } from '@/types/clientProfileUpdates';

export const revalidate = 0;

async function getClientProfileUpdates({
token,
portalId,
}: {
token: string;
portalId: string;
}): Promise<ParsedClientProfileUpdatesResponse[]> {
const res = await fetch(`${apiUrl}/api/client-profile-updates?token=${token}&portalId=${portalId}`);

if (!res.ok) {
throw new Error('Something went wrong while in getClientProfileUpdates');
}

const data = await res.json();

return data;
}

export default async function Home({ searchParams }: { searchParams: { token: string; portalId: string } }) {
const { token, portalId } = searchParams;

const clientProfileUpdates = await getClientProfileUpdates({ token, portalId });

export default function Home() {
return (
<Stack direction="row">
<MainSection />
<MainSection clientProfileUpdates={clientProfileUpdates} />

<SidebarDecider>
<Sidebar />
Expand Down
12 changes: 11 additions & 1 deletion src/app/views/MainSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@ import { Header } from '@/layouts/Header';
import { Box } from '@mui/material';
import { Sidebar } from './Sidebar';
import { TableCore } from '@/components/table/Table';
import { ParsedClientProfileUpdatesResponse } from '@/types/clientProfileUpdates';
import { useEffect } from 'react';

const MainSection = () => {
interface IMainSection {
clientProfileUpdates: ParsedClientProfileUpdatesResponse[];
}

const MainSection = ({ clientProfileUpdates }: IMainSection) => {
const appState = useAppState();
const windowWidth = useWindowWidth();

useEffect(() => {
appState?.setAppState((prev) => ({ ...prev, clientProfileUpdates }));
}, [clientProfileUpdates]);

Check warning on line 22 in src/app/views/MainSection.tsx

View workflow job for this annotation

GitHub Actions / Run linters

React Hook useEffect has a missing dependency: 'appState'. Either include it or remove the dependency array

return (
<Box
sx={{
Expand Down
177 changes: 86 additions & 91 deletions src/components/table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,115 +3,110 @@ import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css'; // Core CSS
import 'ag-grid-community/styles/ag-theme-quartz.css'; // Theme
import './table.css';
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { ClientCellRenderer } from './cellRenderers/ClientCellRenderer';
import { CompanyCellRenderer } from './cellRenderers/CompanyCellRenderer';
import { HistoryCellRenderer } from './cellRenderers/HistoryCellRenderer';
import { useAppState } from '@/hooks/useAppState';
import { getTimeAgo } from '@/utils/getTimeAgo';

export const TableCore = () => {
const appState = useAppState();

// Row Data: The data to be displayed.
const [rowData, setRowData] = useState<any>([
{
Client: { image: 'https://robohash.org/stefan-one', name: 'Tesla', email: '[email protected]', id: 1 },
company: { image: 'https://robohash.org/generate', name: 'Chase' },
'Last updated': '1m',
'Phone number': { value: '1928366444', showDot: true },
Address: 'Texas',
Hobby: 'Music',
},
{
Client: { image: 'https://robohash.org/stefan-eleven', name: 'Wongchi', email: '[email protected]', id: 2 },
company: { image: 'https://robohash.org/generate', name: 'Amazon' },
'Last updated': '1d',
'Phone number': { value: '9283774466', showDot: false },
Address: '',
Hobby: 'Dance',
},
{
Client: { image: 'https://robohash.org/stefan-hundred', name: 'Holyland', email: '[email protected]', id: 3 },
company: { image: 'https://robohash.org/generate', name: 'Walt Disney Co.' },
'Last updated': '',
'Phone number': { value: '2883743322', showDot: true },
Address: 'New York',
Hobby: 'Flying',
},
{
Client: { image: 'https://robohash.org/stefan-five', name: 'Nokia', email: '[email protected]', id: 4 },
company: { image: 'https://robohash.org/generate', name: 'Scrappy' },
'Last updated': '2d',
'Phone number': { value: '9182663344', showDot: true },
Address: 'Hongkong',
Hobby: '',
},
{
Client: { image: 'https://robohash.org/stefan-four', name: 'Prolink', email: '[email protected]', id: 5 },
company: { image: 'https://robohash.org/generate', name: 'Facebook' },
'Last updated': '1 month',
'Phone number': { value: '', showDot: false },
Address: 'Manila',
Hobby: 'Diving',
},
{
Client: { image: 'https://robohash.org/stefan-three', name: 'Apple', email: '[email protected]', id: 6 },
company: { image: 'https://robohash.org/generate', name: 'Catch' },
'Last updated': '12 hours',
'Phone number': { value: '3847228833', showDot: false },
Address: 'Kathmandu',
Hobby: 'Music',
chobby: 'Music',
},
]);
const [rowData, setRowData] = useState<any>([]);
//
// Column Definitions: Defines & controls grid columns.
const [colDefs, setColDefs] = useState<any>([]);

useEffect(() => {
setRowData(appState?.clientProfileUpdates);

const comparator = (valueA: any, valueB: any) => {
if (valueA < valueB) {
let colDefs: any = [];
if (appState?.clientProfileUpdates.length && appState?.clientProfileUpdates.length) {
const col = appState?.clientProfileUpdates[0];
delete col.id;
const keys = Object.keys(col);
keys.map((el) => {
if (el === 'client') {
colDefs = [
...colDefs,
{
field: 'client',
cellRenderer: ClientCellRenderer,
flex: 1,
comparator: comparatorTypeI,
minWidth: 250,
valueGetter: (params: any) => {
const client = params.data[el];
return {
avatarImageUrl: client.avatarImageUrl,
name: client.name,
email: client.email,
};
},
},
];
return;
}
if (el === 'company') {
colDefs = [
...colDefs,
{
field: 'company',
cellRenderer: CompanyCellRenderer,
flex: 1,
comparator: comparatorTypeI,
valueGetter: (params: any) => {
const company = params.data[el];
return {
iconImageUrl: company.iconImageUrl,
name: company.name,
};
},
},
];
return;
}
if (el === 'lastUpdated') {
colDefs = [
...colDefs,
{
field: 'lastUpdated',
flex: 1,
valueGetter: (params: any) => {
const lastUpdated = params.data[el];
return `${getTimeAgo(lastUpdated)} ago`;
},
},
];
return;
}

colDefs = [
...colDefs,
{
field: el,
flex: 1,
cellRenderer: HistoryCellRenderer,
},
];
});
setColDefs(colDefs);
}
}, [appState?.clientProfileUpdates]);

const comparatorTypeI = (valueA: any, valueB: any) => {
if (valueA.name < valueB.name) {
return -1;
}
if (valueA > valueB) {
if (valueA.name > valueB.name) {
return 1;
}

return 0; // names are equal
};

// Column Definitions: Defines & controls grid columns.
const [colDefs, setColDefs] = useState<any>([
{
field: 'Client',
cellRenderer: ClientCellRenderer,
flex: 1,
comparator,
valueGetter: (params: any) => {
const client = params.data.Client;
return `${client.image} ${client.name} ${client.email}`;
},
},
{
field: 'company',
cellRenderer: CompanyCellRenderer,
flex: 1,
comparator,
valueGetter: (params: any) => {
const company = params.data.company;
return `${company.image} ${company.name}`;
},
},
{ field: 'Last updated', flex: 1 },
{
field: 'Phone number',
flex: 1,
cellRenderer: HistoryCellRenderer,
valueGetter: (params: any) => {
const phoneProps = params.data['Phone number'];
return `${phoneProps.value} ${phoneProps.showDot}`;
},
},
{ field: 'Address', flex: 1 },
{ field: 'Hobby', flex: 1 },
]);

const defaultColDef = useMemo(() => {
return {
resizable: false,
Expand Down
10 changes: 5 additions & 5 deletions src/components/table/cellRenderers/ClientCellRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Box, Stack, Typography } from '@mui/material';

export const ClientCellRenderer = ({ value }: { value: string }) => {
const c = value.split(' ');
export const ClientCellRenderer = ({ value }: { value: { avatarImageUrl: string; email: string; name: string } }) => {
const { avatarImageUrl, email, name } = value;
return (
<Stack direction="row" alignItems="center" gap={3} marginTop="10px">
<Box component="img" src={c[0]} alt="avatar" sx={{ width: '28px', height: '28px' }} />
<Box component="img" src={avatarImageUrl} alt="avatar" sx={{ width: '28px', height: '28px' }} />
<Stack direction="column">
<Typography variant="sm" lineHeight={'16px'}>
{c[1]}
{name}
</Typography>
<Typography variant="bodySm" lineHeight={'16px'}>
{c[2]}
{email}
</Typography>
</Stack>
</Stack>
Expand Down
8 changes: 4 additions & 4 deletions src/components/table/cellRenderers/CompanyCellRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Box, Stack, Typography } from '@mui/material';

export const CompanyCellRenderer = ({ value }: { value: string }) => {
const c = value.split(' ');
export const CompanyCellRenderer = ({ value }: { value: { iconImageUrl: string; name: string } }) => {
const { iconImageUrl, name } = value;
return (
<Stack direction="row" alignItems="center" gap={3} marginTop="15px">
<Box component="img" src={c[0]} alt="avatar" sx={{ width: '20px', height: '20px' }} />
<Box component="img" src={iconImageUrl} alt="avatar" sx={{ width: '20px', height: '20px' }} />
<Typography variant="sm" lineHeight={'16px'}>
{c[1]}
{name}
</Typography>
</Stack>
);
Expand Down
Loading

0 comments on commit 0bf24d9

Please sign in to comment.