Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/integrate actions with sentry #9

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
21 changes: 0 additions & 21 deletions .vscode/tasks.json

This file was deleted.

Binary file added app/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"clsx": "^2.1.1",
"lucide-react": "^0.445.0",
"next": "^15.1.0",
"next-safe-action": "^7.10.2",
"next-themes": "^0.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
Expand Down
32 changes: 32 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Customer" ALTER COLUMN "state" SET DATA TYPE VARCHAR(3);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- DropIndex
DROP INDEX "Customer_phone_key";
4 changes: 2 additions & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ model Customer {
firstName String
lastName String
email String @unique
phone String @unique
phone String
address1 String
address2 String?
city String
state String @db.VarChar(2)
state String @db.VarChar(3)
zip String @db.VarChar(10)
notes String?
active Boolean @default(true)
Expand Down
20 changes: 20 additions & 0 deletions src/features/customer/helpers/customer.codex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { CustomerFields } from '@/features/customer/helpers/customer.type';

export const customerCodex = {
toPayload(input: CustomerFields): CustomerFields {
return {
...(input.id === 0 ? {} : { id: input.id }),
active: input.active,
firstName: input.firstName,
lastName: input.lastName,
address1: input.address1,
city: input.city,
state: input.state,
zip: input.zip,
phone: input.phone,
email: input.email,
notes: input.notes?.trim() ?? undefined,
address2: input.address2?.trim() ?? undefined,
};
},
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from 'zod';

export const CustomerSchema = z.object({
id: z.number().optional(),
firstName: z.string().min(1, 'First name is required'),
lastName: z.string().min(1, 'Last name is required'),
address1: z.string().min(1, 'Address is required'),
Expand All @@ -9,7 +10,7 @@ export const CustomerSchema = z.object({
state: z.string().length(3, 'State must be exactly 3 characters'),
email: z.string().email('Invalid email address'),
zip: z.string().regex(/^\d{5}$/, 'Invalid Zip code. Use 5 digits'),
phone: z.string().regex(/^\d{10}$/, 'Invalid phone number. Use 10 digits'),
phone: z.string(),
notes: z.string().optional(),
active: z.boolean().default(true),
});
Expand Down
33 changes: 33 additions & 0 deletions src/features/customer/services/customer.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use server';

import { customerCodex } from '@/features/customer/helpers/customer.codex';
import { CustomerSchema } from '@/features/customer/helpers/customer.type';
import { createCustomer, updateCustomer } from '@/features/customer/services/customer.query';
import { actionClient } from '@/lib/safe-action';
import { getKindeServerSession } from '@kinde-oss/kinde-auth-nextjs/server';
import { flattenValidationErrors } from 'next-safe-action';
import { redirect } from 'next/navigation';

export const saveCustomerAction = actionClient
.metadata({ actionName: 'createCustomer' })
.schema(CustomerSchema, {
handleValidationErrorsShape: async (ve) => flattenValidationErrors(ve).fieldErrors,
})
.action(async ({ parsedInput }) => {
const { isAuthenticated } = getKindeServerSession();
const isAuth = await isAuthenticated();

if (!isAuth) {
redirect('/login');
}

const payload = customerCodex.toPayload(parsedInput);

if (payload.id) {
const result = await updateCustomer(payload);
return { data: result, message: `Customer ID #${result.id} updated successfully` };
}

const result = await createCustomer(payload);
return { data: result, message: `Customer ${result.id} created successfully` };
});
30 changes: 30 additions & 0 deletions src/lib/safe-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as Sentry from '@sentry/nextjs';
import { createSafeActionClient } from 'next-safe-action';
import { z } from 'zod';

export const actionClient = createSafeActionClient({
defineMetadataSchema() {
return z.object({
actionName: z.string(),
});
},
handleServerError(error, utils) {
const { clientInput, metadata } = utils;

Sentry.captureException(error, (scope) => {
scope.clear();
scope.setContext('serverError', { message: error.message });
scope.setContext('metadata', { actionName: metadata.actionName });
scope.setContext('clientInput', { clientInput });
return scope;
});

// We don't want to leak any sensitive data
if (error.constructor.name === 'DatabaseError') {
return 'Database Error: Your data did not save. Support will be notified.';
}

console.error('Action error:', error.message);
return error.message;
},
});
4 changes: 2 additions & 2 deletions user-stories.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
13. [X] Users can have Employee, Manager, or Admin permissions
14. [ ] All users can create and view tickets
15. [ ] All users can create, edit and view customers
16. [ ] Employees can only edit their assigned tickets
17. [ ] Managers and Admins can view, edit, and delete all tickets
16. [X] Employees can only edit their assigned tickets
17. [X] Managers and Admins can view, edit, and complete all tickets
18. [ ] Desktop mode is most important but the app should be usable on tablet devices as well.
19. [X] Light / Dark mode option requested by employees
20. [X] Expects quick support if anything goes wrong with the app