Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions .env.example

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@ CREATE TABLE "Setting" (
"createdById" UUID NOT NULL,
"message" TEXT,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"updatedAt" TIMESTAMPTZ NOT NULL,

CONSTRAINT "Setting_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Setting_createdById_key" ON "Setting"("createdById");
-- CreateTable
CREATE TABLE "Message" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"message" TEXT NOT NULL,
"clientId" UUID NOT NULL,
"channelId" UUID NOT NULL,
"senderId" UUID NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ NOT NULL,

CONSTRAINT "Message_pkey" PRIMARY KEY ("id")
);

14 changes: 0 additions & 14 deletions migrations/20231020073838_create_message_table/migration.sql

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
UPDATE "Setting" SET "senderId"="createdById";
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Setting" ADD COLUMN "senderId" UUID;
17 changes: 13 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,27 @@
"prettier:check": "prettier --check \"{src,test}/**/*.{ts,tsx}\"",
"prettier:fix": "prettier --write .",
"lint-staged": "npx lint-staged",
"postinstall": "prisma generate"
"postinstall": "prisma generate",
"db:migrate": "dotenv-run-script .env.development.local -- db:_migrate",
"db:_migrate": "prisma migrate dev",
"db:seed": "dotenv-run-script .env.development.local -- db:_seed",
"db:_seed": "prisma migrate resolve --applied 0_init"
},
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.2",
"@js-joda/core": "^5.6.1",
"@js-joda/timezone": "^2.18.2",
"@prisma/client": "^5.4.2",
"@mui/material": "^5.15.7",
"@prisma/client": "^5.9.1",
"@radix-ui/react-select": "^2.0.0",
"@vercel/postgres": "^0.5.0",
"copilot-node-sdk": "^0.0.45",
"copilot-node-sdk": "1.1.3",
"dotenv-run-script": "^0.4.1",
"next": "^14.0.4",
"next-plugin-svgr": "^1.1.8",
"prisma": "^5.4.2",
"prisma": "^5.9.1",
"react": "latest",
"react-dom": "latest",
"react-error-boundary": "^4.0.11",
Expand All @@ -49,6 +57,7 @@
"prettier": "^3.1.1",
"prisma": "^5.4.2",
"tailwindcss": "latest",
"ts-node": "^10.9.2",
"typescript": "latest"
},
"lint-staged": {
Expand Down
1 change: 1 addition & 0 deletions schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ model Setting {
timezone String?
workingHours Json? @db.JsonB
createdById String @db.Uuid
senderId String? @db.Uuid
message String?
createdAt DateTime @default(now()) @db.Timestamptz()
updatedAt DateTime @updatedAt @ignore @db.Timestamptz()
Expand Down
22 changes: 22 additions & 0 deletions src/app/api/internal-users/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { errorHandler } 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 token = searchParams.get('token');
if (!token) {
return errorHandler('Missing token', 422);
}

const copilotClient = new CopilotAPI(z.string().parse(token));
try {
const clients = await copilotClient.getInternalUsers();

return NextResponse.json(clients);
} catch (error) {
console.error('getInternalUsers', error);
return errorHandler('Clients not found.', 404);
}
}
4 changes: 2 additions & 2 deletions src/app/api/messages/services/message.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class MessageService {
async sendMessage(copilotClient: CopilotAPI, setting: SettingResponse, message: Message): Promise<void> {
const messageData = SendMessageRequestSchema.parse({
text: setting.message,
senderId: setting.createdById,
senderId: setting.senderId,
channelId: message.channelId,
});

Expand All @@ -76,7 +76,7 @@ export class MessageService {
message: z.string().parse(setting.message),
clientId: message.senderId,
channelId: messageData.channelId,
senderId: setting.createdById,
senderId: setting.senderId,
},
}),
]);
Expand Down
3 changes: 3 additions & 0 deletions src/app/api/settings/services/setting.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class SettingService {

async save(requestData: SettingRequest, { apiToken }: { apiToken: string }): Promise<void> {
const currentUser = await getCurrentUser(apiToken);
console.log(requestData);

const settingByUser = await this.prismaClient.setting.findFirst({
where: {
Expand All @@ -38,6 +39,7 @@ export class SettingService {
workingHours: requestData.workingHours,
message: requestData.message,
createdById: currentUser.id,
senderId: requestData.senderId,
},
});

Expand All @@ -54,6 +56,7 @@ export class SettingService {
// @ts-ignore
workingHours: requestData.workingHours,
message: requestData.message,
senderId: requestData.senderId,
},
});
}
Expand Down
46 changes: 39 additions & 7 deletions src/app/components/AutoResponder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
SelectedDay,
SettingsData,
} from '@/constants';
import { InternalUser, InternalUserSchema, InternalUsers } from '@/types/common';
import { MenuItem, Select, SelectChangeEvent } from '@mui/material';

const defaultSelectedDays: SelectedDay[] = [
{
Expand Down Expand Up @@ -56,6 +58,7 @@ const defaultSelectedDays: SelectedDay[] = [
interface Props {
onSave(data: SettingsData): Promise<void>;
activeSettings: SettingsData;
internalUsers: InternalUsers;
}
const DropdownIndicator = (props: DropdownIndicatorProps<ITimezone, false, GroupBase<ITimezone>>) => {
return (
Expand Down Expand Up @@ -145,10 +148,10 @@ const ValidationSchema = z.object({
.min(10, "Response can't be less than 10 characters long")
.max(2000, "Response can't be more than 2000 characters long")
.nullable(),
sender: z.string(),
senderId: z.string().uuid().nullable(),
});

const AutoResponder = ({ onSave, activeSettings }: Props) => {
const AutoResponder = ({ onSave, activeSettings, internalUsers }: Props) => {
const defaultFormValues = useRef(activeSettings);
const [saving, setSaving] = useState(false);
const [workingHoursErrors, setWorkingHoursErrors] = useState<Record<number, string>>({});
Expand Down Expand Up @@ -261,6 +264,8 @@ const AutoResponder = ({ onSave, activeSettings }: Props) => {
setWorkingHoursErrors({});
};

console.log(errors);

return (
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<FormProvider {...methods}>
Expand Down Expand Up @@ -335,11 +340,38 @@ const AutoResponder = ({ onSave, activeSettings }: Props) => {
{errors.response && <p className="text-red-500 text-xs">{errors.response.message}</p>}
</div>
<Typography text="Sent by" variant="label" className="mb-1.5 mt-6" />
<input
disabled
placeholder="Your name"
className="block w-full p-3 text-[14px] font-normal rounded-md bg-transparent border border-border-disabled mb-8 disabled:text-text-disabled"
{...register('sender')}
<Controller
name="senderId"
render={({ field: { onChange, value } }) => (
<Select
fullWidth
labelId="internal-users-select-label"
id="internal-users-select"
value={value}
label=""
onChange={(e: SelectChangeEvent) => {
onChange(e.target.value);
}}
sx={{
'& .MuiOutlinedInput-input': {
padding: '6px 12px',
},
'.MuiOutlinedInput-notchedOutline': {
borderColor: 'rgb(201 203 205)',
},
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
borderColor: '#C9CBCD',
},
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: '#C9CBCD',
},
}}
>
{internalUsers.data?.map((user: InternalUser) => {
return <MenuItem key={user.id} value={user.id}>{`${user.givenName} ${user.familyName}`}</MenuItem>;
})}
</Select>
)}
/>
</Fieldset>
)}
Expand Down
27 changes: 23 additions & 4 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ import { SettingResponse } from '@/types/setting';
import AutoResponder from '@/app/components/AutoResponder';
import { SettingService } from '@/app/api/settings/services/setting.service';
import { CopilotAPI } from '@/utils/copilotApiUtils';
import { ClientResponse, CompanyResponse, MeResponse } from '@/types/common';
import { ClientResponse, CompanyResponse, MeResponse, InternalUsers, InternalUser } from '@/types/common';
import { z } from 'zod';
import appConfig from '@/config/app';

type SearchParams = { [key: string]: string | string[] | undefined };

const settingsService = new SettingService();

async function getContent(searchParams: SearchParams) {
if (!searchParams.token) {
throw new Error('Missing token');
return {
client: undefined,
company: undefined,
me: undefined,
};
}

const copilotAPI = new CopilotAPI(z.string().parse(searchParams.token));
Expand All @@ -33,7 +38,7 @@ async function getContent(searchParams: SearchParams) {
return result;
}

const populateSettingsFormData = (settings: SettingResponse): Omit<SettingsData, 'sender'> => {
const populateSettingsFormData = (settings: SettingResponse): SettingsData => {
return {
autoRespond: settings?.type || $Enums.SettingType.DISABLED,
response: settings?.message || null,
Expand All @@ -43,11 +48,24 @@ const populateSettingsFormData = (settings: SettingResponse): Omit<SettingsData,
startHour: workingHour.startTime as HOUR,
endHour: workingHour.endTime as HOUR,
})),
senderId: settings?.senderId,
};
};

async function getInternalUsers(token: string): Promise<InternalUsers> {
const res = await fetch(`${appConfig.apiUrl}/api/internal-users?token=${token}`);
return await res.json();
}

export default async function Page({ searchParams }: { searchParams: SearchParams }) {
const { me } = await getContent(searchParams);
const internalUsers = await getInternalUsers(searchParams.token as string);

let internalUsersWithClientAccessLimitedFalse: InternalUsers = { data: [] };
if (internalUsers.data) {
let _internalUsers = internalUsers.data.filter((user: InternalUser) => user.isClientAccessLimited !== true);
internalUsersWithClientAccessLimitedFalse = { data: _internalUsers };
}

const setting = await settingsService.findByUserId(me?.id as string);
const saveSettings = async (data: SettingsData) => {
Expand All @@ -63,6 +81,7 @@ export default async function Page({ searchParams }: { searchParams: SearchParam
endTime: selectedDay.endHour,
}))
: data.selectedDays,
senderId: data.senderId,
};
await settingsService.save(setting, {
apiToken: z.string().parse(searchParams.token),
Expand All @@ -75,8 +94,8 @@ export default async function Page({ searchParams }: { searchParams: SearchParam
onSave={saveSettings}
activeSettings={{
...populateSettingsFormData(setting as SettingResponse),
sender: `${me?.givenName} ${me?.familyName}`,
}}
internalUsers={internalUsersWithClientAccessLimitedFalse}
/>
</main>
);
Expand Down
1 change: 1 addition & 0 deletions src/config/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const appConfig = {
copilotApiKey: process.env.COPILOT_API_KEY || '',
webhookSigningSecret: process.env.WEBHOOK_SIGNING_SECRET || '',
apiUrl: `${process.env.VERCEL_ENV === 'development' ? 'http://' : 'https://'}${process.env.VERCEL_URL}`,
};

export default appConfig;
2 changes: 1 addition & 1 deletion src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ export interface SettingsData {
timezone: string | null;
selectedDays: SelectedDay[] | null;
response: string | null;
sender: string | null;
senderId: string;
}
Loading