Skip to content

Commit

Permalink
OUT-46 Integrate APIs (#11)
Browse files Browse the repository at this point in the history
* data plotting of table

* data plotting of table

* integrated custom field access and save apis

* fix custom field access issues

* added client api

* added manage page

* added history popup

* added sorting feature

* added searching feature

* added searching for all fields

* added chip color variations

* fix bug

* added empty state fallback

* fix bug

* fix design issues

* fix design issues

* adds tooltip on disabled field

* removed image if company image icon is not present

* fix bugs

* added enums for ProfileLinks

* added enums for Permissions

* removed unnecessary if/else comparisions

* adds server action

* fix issue with server action

* fix issue with server action

* added token and portalId

* stringified in client component

* stringified in client component

* reverted from server action for now
  • Loading branch information
aatbip authored Feb 9, 2024
1 parent e5dab64 commit 668e757
Show file tree
Hide file tree
Showing 36 changed files with 1,228 additions and 274 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 @@ -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;
`;
}
}
25 changes: 25 additions & 0 deletions src/app/api/client/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { respondError } from '@/utils/common';
import { CopilotAPI } from '@/utils/copilotApiUtils';
import { NextResponse, NextRequest } from 'next/server';
import { z } from 'zod';

export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const clientId = searchParams.get('clientId');
const token = searchParams.get('token');
if (!token) {
return respondError('Missing token', 422);
}
if (!clientId) {
respondError('Missing client Id', 422);
}
const copilotClient = new CopilotAPI(z.string().parse(token));
try {
const client = await copilotClient.getClient(z.string().parse(clientId));

return NextResponse.json({ data: client });
} catch (error) {
console.log(error);
return respondError('Client not found.', 404);
}
}
6 changes: 5 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import './globals.css';
import ThemeRegistry from './ThemeRegistry';
import { AppContextProvider } from '@/context';
import { ToggleDecider } from '@/hoc/ToggleDecider';
import { Footer } from '@/layouts/Footer';

const inter = Inter({ subsets: ['latin'] });

Expand All @@ -18,7 +19,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<AppContextProvider>
<body className={inter.className}>
<ThemeRegistry options={{ key: 'mui' }}>
<ToggleDecider>{children}</ToggleDecider>
<ToggleDecider>
{children}
<Footer />
</ToggleDecider>
</ThemeRegistry>
</body>
</AppContextProvider>
Expand Down
89 changes: 79 additions & 10 deletions src/app/manage/page.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,96 @@
import { Box, Stack, Typography } from '@mui/material';
import { ManagePageContainer } from './views/ManagePageContainer';
import { SimpleButton } from '@/components/styled/SimpleButton';
import { apiUrl } from '@/config';
import { CustomFieldAccessResponse } from '@/types/customFieldAccess';
import { Settings } from '@mui/icons-material';
import { ProfileLinks } from '@/types/settings';

export const revalidate = 0;

async function getSettings({ token, portalId }: { token: string; portalId: string }) {
const res = await fetch(`${apiUrl}/api/settings?token=${token}&portalId=${portalId}`);

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

const { data } = await res.json();

return data;
}

async function getCustomFieldAccess({
token,
portalId,
}: {
token: string;
portalId: string;
}): Promise<CustomFieldAccessResponse> {
const res = await fetch(`${apiUrl}/api/custom-field-access?token=${token}&portalId=${portalId}`);

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

const { data } = await res.json();

return data;
}

async function getClient(clientId: string, token: string) {
const res = await fetch(`${apiUrl}/api/client?clientId=${clientId}&token=${token}`);
if (!res.ok) {
throw new Error(`No client found with '${token}' token`);
}
const { data } = await res.json();
return data;
}

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

const settings = await getSettings({ token, portalId }).then((s) => s.profileLinks);
const customFieldAccess = await getCustomFieldAccess({ token, portalId });
// static for now, will be dynamic later after some API decisions are made
const clientId = 'a583a0d0-de70-4d14-8bb1-0aacf7424e2c';
const companyId = '52eb75a9-2790-4e37-aa7a-c13f7bc3aa91';
// const clientId = '2b37da9b-73b9-4c28-b7ac-144cf39cb13b';
// const companyId = 'b5b3883c-f3e7-40e2-98e8-4f4b195ba98e';
const client = await getClient(clientId, token);

export default function ManagePage() {
return (
<Box
sx={{
padding: { xs: '32px 16px', md: '124px 236px' },
padding: { xs: '32px 16px', md: '90px 236px' },
}}
>
<Typography variant="xl">Manage your profile</Typography>

<ManagePageContainer />
<ManagePageContainer
customFieldAccess={customFieldAccess}
client={client}
token={token}
companyId={companyId}
clientId={clientId}
portalId={portalId}
/>

<Stack direction="column" mt={16} rowGap={4}>
<Typography variant="xl">Other settings</Typography>
{settings && (settings.includes(ProfileLinks.PaymentMethod) || settings.includes(ProfileLinks.ProfileSetting)) && (
<Typography variant="xl">Other settings</Typography>
)}
<Stack direction="row" columnGap={4}>
<SimpleButton>
<Typography variant="md">Set a payment method</Typography>
</SimpleButton>
<SimpleButton>
<Typography variant="md">Go to account settings</Typography>
</SimpleButton>
{settings && settings.includes(ProfileLinks.PaymentMethod) && (
<SimpleButton>
<Typography variant="md">Set a payment method</Typography>
</SimpleButton>
)}
{settings && settings.includes(ProfileLinks.ProfileSetting) && (
<SimpleButton>
<Typography variant="md">Go to account settings</Typography>
</SimpleButton>
)}
</Stack>
</Stack>
</Box>
Expand Down
Loading

0 comments on commit 668e757

Please sign in to comment.