Skip to content

Commit

Permalink
added manage page
Browse files Browse the repository at this point in the history
  • Loading branch information
aatbip committed Feb 2, 2024
1 parent 77c70f6 commit 9460e0a
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export class ClientProfileUpdatesService {
private prismaClient: PrismaClient = DBClient.getInstance();

async save(requestData: ClientProfileUpdates): Promise<void> {
//@ts-expect-error prisma generate unexpected error
await this.prismaClient.clientProtileUpdates.create({
await this.prismaClient.clientProfileUpdates.create({
data: {
clientId: requestData.clientId,
companyId: requestData.companyId,
Expand Down
81 changes: 72 additions & 9 deletions src/app/manage/page.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,90 @@
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';

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 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>
<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('profile_settings') && (
<SimpleButton>
<Typography variant="md">Set a payment method</Typography>
</SimpleButton>
)}
{settings && settings.includes('payment_method') && (
<SimpleButton>
<Typography variant="md">Go to account settings</Typography>
</SimpleButton>
)}
</Stack>
</Stack>
</Box>
Expand Down
190 changes: 155 additions & 35 deletions src/app/manage/views/ManagePageContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,169 @@
'use client';

import { FooterSave } from '@/components/footerSave/FooterSave';
import { MultiSelect } from '@/components/multiSelect/MultiSelect';
import { StyledTextInput } from '@/components/styled/StyledTextInput';
import { apiUrl } from '@/config';
import { Stack, Typography, styled } from '@mui/material';
import { useMemo, useState } from 'react';

export const ManagePageContainer = ({
customFieldAccess,
client,
token,
clientId,
companyId,
portalId,
}: {
customFieldAccess: any;
client: any;
token: string;
clientId: string;
companyId: string;
portalId: string;
}) => {
const [_customFieldAccess, setCustomFieldAccess] = useState<any>(customFieldAccess);

const [loading, setLoading] = useState(false);

const [allowedCustomField, setAllowedCustomField] = useState<any>(null);

const customFieldsValue: any | null = client.customFields;

const [profileData, setProfileData] = useState<any>({});

const [originalProfileData, setOriginalProfileData] = useState<any>({});

useMemo(() => {
if (customFieldAccess.length > 0) {
const allowedFields = customFieldAccess.filter((field: any) => field.permission?.length > 0);
const getSelectedValuesForMultiSelect = (key: string) => {
const values = customFieldsValue[key];
if (!values) {
return [];
}
const customFieldObject = allowedFields.find((el: any) => el.key === key);
const selectedValues = customFieldObject.options.filter((el: any) => values.includes(el.key));

return selectedValues;
};
allowedFields.map((field: any) => {
profileData[field.key] =
field.type === 'multiSelect'
? [...getSelectedValuesForMultiSelect(field.key)]
: customFieldsValue[field.key] || '';
});
setOriginalProfileData(profileData);
setAllowedCustomField(allowedFields);
} else {
setAllowedCustomField([]);
}
}, [_customFieldAccess]);

Check warning on line 61 in src/app/manage/views/ManagePageContainer.tsx

View workflow job for this annotation

GitHub Actions / Run linters

React Hook useMemo has missing dependencies: 'customFieldAccess', 'customFieldsValue', and 'profileData'. Either include them or remove the dependency array. You can also replace multiple useState variables with useReducer if 'setOriginalProfileData' needs the current value of 'profileData'

const handleCancel = () => {
setProfileData({
...originalProfileData,
});
};

const handleSave = async () => {
setLoading(true);
function transformObject(obj: any) {
const result: any = {};
for (const key in obj) {
if (Array.isArray(obj[key])) {
result[key] = obj[key].map((item: any) => item.key);
} else {
result[key] = obj[key];
}
}
return result;
}
const form = transformObject(profileData);

await fetch('/api/client-profile-updates', {
method: 'POST',
body: JSON.stringify({
token: token,
companyId: companyId,
clientId: clientId,
portalId: portalId,
form: form,
}),
});

const res = await fetch(`/api/custom-field-access?token=${token}&portalId=${portalId}`);

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

setCustomFieldAccess(data);

setLoading(false);
};

export const ManagePageContainer = () => {
return (
<Stack
direction="column"
sx={{
padding: { xs: 4, sm: 6 },
rowGap: 6,
border: (theme) => `1px solid ${theme.color.borders.border}`,
borderRadius: (theme) => theme.shape.radius100,
mt: 4,
}}
>
<InputContainer>
<Typography variant="md">Phone number</Typography>
<StyledTextInput variant="outlined" padding="8px 12px" />
</InputContainer>
<InputContainer>
<Typography variant="md">Website</Typography>
<StyledTextInput variant="outlined" padding="8px 12px" />
</InputContainer>
<InputContainer>
<Typography variant="md">Website</Typography>
<StyledTextInput variant="outlined" padding="8px 12px" />
</InputContainer>
<InputContainer>
<MultiSelect<{ name: string }> data={data} chipColor="rgb(0, 0, 255)" nameField={(item) => item.name} />
</InputContainer>
</Stack>
<>
<Stack
direction="column"
sx={{
padding: { xs: 4, sm: 6 },
rowGap: 6,
border: (theme) => `1px solid ${theme.color.borders.border}`,
borderRadius: (theme) => theme.shape.radius100,
mt: 4,
}}
>
{allowedCustomField &&
allowedCustomField.map((field: any, key: number) => {
if (field.type !== 'multiSelect') {
return (
<InputContainer key={key}>
<Typography variant="md">{field.name}</Typography>
<StyledTextInput
value={profileData && profileData[field.key]}
variant="outlined"
padding="8px 12px"
disabled={!field.permission.includes('EDIT')}
key={key}
onChange={(e) => {
setProfileData((prev: any) => {
return { ...prev, [field.key]: e.target.value };
});
}}
type="text"
/>
</InputContainer>
);
}
if (field.type === 'multiSelect') {
return (
<InputContainer key={key}>
<Typography variant="md">{field.name}</Typography>
<MultiSelect<{ label: string }>
key={key}
data={field.options}
chipColor="rgb(0, 0, 255)"
nameField={(item) => item.label}
value={profileData && profileData[field.key]}
getSelectedValue={(value) => {
setProfileData((prev: any) => {
return { ...prev, [field.key]: value };
});
}}
/>
</InputContainer>
);
}
})}
</Stack>
{JSON.stringify(originalProfileData) === JSON.stringify(profileData) ? null : (
<FooterSave type="2" loading={loading} handleSave={handleSave} handleCancel={handleCancel} />
)}
</>
);
};

const InputContainer = styled(Stack)({
flexDirection: 'column',
rowGap: 1.33,
});

const data: { name: string }[] = [
{ name: 'Option 1' },
{ name: 'Option 2' },
{ name: 'Option 3' },
{ name: 'Option 4' },
{ name: 'Option 5' },
];
63 changes: 63 additions & 0 deletions src/components/footerSave/FooterSave.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Stack, Typography } from '@mui/material';
import { VariantButton1, VariantButton2 } from '../styled/VariantButton';

export const FooterSave = ({
loading,
handleSave,
handleCancel,
type,
}: {
loading: boolean;
handleSave: () => void;
handleCancel: () => void;
type: '1' | '2';
}) => {
return (
<Stack
sx={{
width: '100%',
position: 'fixed',
bottom: 0,
left: 0,
borderTop: (theme) => `1px solid ${theme.color.borders.border}`,
padding: '16px 24px',
background: '#fff',
alignItems: 'flex-end',
zIndex: 9999,
}}
>
<Stack direction="row" columnGap={5}>
<VariantButton1 onClick={handleCancel}>
<Typography variant="md">Cancel</Typography>
</VariantButton1>
<VariantButton2
onClick={handleSave}
sx={{
background: type === '1' ? '#212B36' : '#5e57e3',
border: `1px solid ${type === '1' ? '#212B36' : '#5e57e3'}`,
}}
>
{loading ? (
<Typography
variant="md"
sx={{
color: '#fff',
}}
>
Saving...
</Typography>
) : (
<Typography
variant="md"
sx={{
color: '#fff',
}}
>
Save changes
</Typography>
)}
</VariantButton2>
</Stack>
</Stack>
);
};
Loading

0 comments on commit 9460e0a

Please sign in to comment.