Skip to content

Commit 0e3805a

Browse files
committed
feat: enhance type safety and error handling across various components and routes
1 parent 0307ef8 commit 0e3805a

File tree

12 files changed

+101
-53
lines changed

12 files changed

+101
-53
lines changed

src/hooks.server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { redirect } from '@sveltejs/kit';
55
export async function handle({ event, resolve }) {
66
const sessionId = await event.cookies.get('session');
77
// console.log(sessionId, '-----------------sessionid')
8-
let user = false
8+
let user = null;
99
if(sessionId && sessionId!=''){
1010
user = await prisma.user.findFirst({
1111
where: {

src/routes/(app)/app/cases/+page.server.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export const actions = {
4646
const subject = form.get('title')?.toString().trim();
4747
const description = form.get('description')?.toString().trim();
4848
const accountId = form.get('accountId')?.toString();
49-
const dueDate = form.get('dueDate') ? new Date(form.get('dueDate')) : null;
49+
const dueDateValue = form.get('dueDate');
50+
const dueDate = dueDateValue ? new Date(dueDateValue.toString()) : null;
5051
const priority = form.get('priority')?.toString() || 'Medium';
5152
const ownerId = form.get('assignedId')?.toString();
5253
if (!subject || !accountId || !ownerId) {
@@ -70,14 +71,16 @@ export const actions = {
7071
const subject = form.get('title')?.toString().trim();
7172
const description = form.get('description')?.toString().trim();
7273
const accountId = form.get('accountId')?.toString();
73-
const dueDate = form.get('dueDate') ? new Date(form.get('dueDate')) : null;
74+
const dueDateValue = form.get('dueDate');
75+
const dueDate = dueDateValue ? new Date(dueDateValue.toString()) : null;
7476
const priority = form.get('priority')?.toString() || 'Medium';
7577
const ownerId = form.get('assignedId')?.toString();
76-
if (!subject || !accountId || !ownerId) {
78+
const caseId = form.get('caseId')?.toString();
79+
if (!subject || !accountId || !ownerId || !caseId) {
7780
return fail(400, { error: 'Missing required fields.' });
7881
}
7982
await prisma.case.update({
80-
where: { id: params.caseId },
83+
where: { id: caseId, organizationId: locals.org.id },
8184
data: {
8285
subject,
8386
description,
@@ -87,22 +90,33 @@ export const actions = {
8790
ownerId
8891
}
8992
});
90-
throw redirect(303, `/app/cases/${params.caseId}`);
93+
throw redirect(303, `/app/cases/${caseId}`);
9194
},
92-
delete: async ({ params }) => {
93-
await prisma.case.delete({ where: { id: params.caseId } });
95+
delete: async ({ request, locals }) => {
96+
const form = await request.formData();
97+
const caseId = form.get('caseId')?.toString();
98+
if (!caseId) {
99+
return fail(400, { error: 'Case ID is required.' });
100+
}
101+
await prisma.case.delete({
102+
where: {
103+
id: caseId,
104+
organizationId: locals.org.id
105+
}
106+
});
94107
throw redirect(303, '/app/cases');
95108
},
96-
comment: async ({ request, params, locals }) => {
109+
comment: async ({ request, locals }) => {
97110
const form = await request.formData();
98111
const body = form.get('body')?.toString().trim();
99-
if (!body) return fail(400, { error: 'Comment cannot be empty.' });
112+
const caseId = form.get('caseId')?.toString();
113+
if (!body || !caseId) return fail(400, { error: 'Comment and case ID are required.' });
100114
await prisma.comment.create({
101115
data: {
102116
body,
103117
authorId: locals.user.id,
104118
organizationId: locals.org.id,
105-
caseId: params.caseId
119+
caseId: caseId
106120
}
107121
});
108122
return { success: true };

src/routes/(app)/app/cases/new/+page.server.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export const actions = {
3333
const subject = form.get('title')?.toString().trim();
3434
const description = form.get('description')?.toString().trim();
3535
const accountId = form.get('accountId')?.toString();
36-
const dueDate = form.get('dueDate') ? new Date(form.get('dueDate')) : null;
36+
const dueDateValue = form.get('dueDate');
37+
const dueDate = dueDateValue ? new Date(dueDateValue.toString()) : null;
3738
const priority = form.get('priority')?.toString() || 'Medium';
3839
const ownerId = form.get('assignedId')?.toString();
3940
if (!subject || !accountId || !ownerId) {

src/routes/(app)/app/contacts/+page.server.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import prisma from '$lib/prisma';
22
import { error } from '@sveltejs/kit';
3+
import { Prisma } from '@prisma/client';
34

45

56
/** @type {import('./$types').PageServerLoad} */
@@ -18,12 +19,12 @@ export async function load({ url, locals }) {
1819
organizationId: locals.org.id,
1920
...(search && {
2021
OR: [
21-
{ firstName: { contains: search, mode: 'insensitive' } },
22-
{ lastName: { contains: search, mode: 'insensitive' } },
23-
{ email: { contains: search, mode: 'insensitive' } },
24-
{ phone: { contains: search, mode: 'insensitive' } },
25-
{ title: { contains: search, mode: 'insensitive' } },
26-
{ department: { contains: search, mode: 'insensitive' } }
22+
{ firstName: { contains: search, mode: Prisma.QueryMode.insensitive } },
23+
{ lastName: { contains: search, mode: Prisma.QueryMode.insensitive } },
24+
{ email: { contains: search, mode: Prisma.QueryMode.insensitive } },
25+
{ phone: { contains: search, mode: Prisma.QueryMode.insensitive } },
26+
{ title: { contains: search, mode: Prisma.QueryMode.insensitive } },
27+
{ department: { contains: search, mode: Prisma.QueryMode.insensitive } }
2728
]
2829
}),
2930
...(ownerId && { ownerId })
@@ -104,7 +105,7 @@ export const actions = {
104105
try {
105106

106107
const data = await request.formData();
107-
const contactId = data.get('contactId');
108+
const contactId = data.get('contactId')?.toString();
108109

109110
if (!contactId) {
110111
throw error(400, 'Contact ID is required');

src/routes/(app)/app/contacts/+page.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,25 @@
5858
}
5959
6060
// Pagination
61+
/** @param {number} pageNum */
6162
function goToPage(pageNum) {
6263
const params = new URLSearchParams($page.url.searchParams);
6364
params.set('page', pageNum.toString());
6465
goto(`?${params.toString()}`);
6566
}
6667
6768
// Modal functions
69+
/** @param {any} contact */
6870
function editContact(contact) {
6971
goto(`/app/contacts/${contact.id}/edit`);
7072
}
7173
74+
/** @param {string | Date} dateString */
7275
function formatDate(dateString) {
7376
return new Date(dateString).toLocaleDateString();
7477
}
7578
79+
/** @param {string} phone */
7680
function formatPhone(phone) {
7781
if (!phone) return '';
7882
// Basic phone formatting - can be enhanced

src/routes/(app)/app/contacts/[contactId]/+page.server.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ export async function load({ params, locals }) {
44
const org = locals.org;
55
const contact = await prisma.contact.findUnique({
66
where: { id: params.contactId, organizationId: org.id },
7+
include: {
8+
owner: {
9+
select: {
10+
id: true,
11+
name: true,
12+
email: true
13+
}
14+
}
15+
}
716
});
817

918
if (!contact) {

src/routes/(app)/app/contacts/[contactId]/+page.svelte

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,42 @@
11
<script>
22
import { ArrowLeft, Mail, Phone, Building2, Calendar, User, MapPin, Edit, Plus, ExternalLink, Clock, DollarSign, Target, CheckCircle, Circle, AlertCircle, Users, Star } from '@lucide/svelte';
3-
export let data;
4-
const { contact } = data;
3+
4+
/** @type {{ data: import('./$types').PageData }} */
5+
let { data } = $props();
6+
7+
const contact = data.contact;
8+
9+
if (!contact) {
10+
throw new Error('Contact not found');
11+
}
512
613
// Get primary account relationship
714
const primaryAccountRel = contact.accountRelationships?.find(rel => rel.isPrimary);
815
const hasMultipleAccounts = contact.accountRelationships?.length > 1;
916
17+
/** @param {string | Date} dateStr */
1018
function formatDate(dateStr) {
1119
if (!dateStr) return 'N/A';
1220
return new Date(dateStr).toLocaleDateString('en-US', {
1321
year: 'numeric', month: 'short', day: 'numeric'
1422
});
1523
}
1624
25+
/** @param {string | Date} dateStr */
1726
function formatDateTime(dateStr) {
1827
if (!dateStr) return 'N/A';
1928
return new Date(dateStr).toLocaleDateString('en-US', {
2029
year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit'
2130
});
2231
}
2332
33+
/** @param {number} amount */
2434
function formatCurrency(amount) {
2535
if (!amount) return '$0';
2636
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount);
2737
}
2838
39+
/** @param {string} status */
2940
function getStatusColor(status) {
3041
const colors = {
3142
'Completed': 'text-green-600 bg-green-50 dark:bg-green-900/20',
@@ -36,16 +47,17 @@
3647
'NEGOTIATION': 'text-orange-600 bg-orange-50 dark:bg-orange-900/20',
3748
'PROPOSAL': 'text-purple-600 bg-purple-50 dark:bg-purple-900/20'
3849
};
39-
return colors[status] || 'text-gray-600 bg-gray-50 dark:bg-gray-900/20';
50+
return colors[/** @type {keyof typeof colors} */ (status)] || 'text-gray-600 bg-gray-50 dark:bg-gray-900/20';
4051
}
4152
53+
/** @param {string} priority */
4254
function getPriorityColor(priority) {
4355
const colors = {
4456
'High': 'text-red-600 bg-red-50 dark:bg-red-900/20',
4557
'Normal': 'text-blue-600 bg-blue-50 dark:bg-blue-900/20',
4658
'Low': 'text-gray-600 bg-gray-50 dark:bg-gray-900/20'
4759
};
48-
return colors[priority] || 'text-gray-600 bg-gray-50 dark:bg-gray-900/20';
60+
return colors[/** @type {keyof typeof colors} */ (priority)] || 'text-gray-600 bg-gray-50 dark:bg-gray-900/20';
4961
}
5062
</script>
5163
@@ -256,7 +268,7 @@
256268
<div class="text-right">
257269
<p class="font-medium text-gray-900 dark:text-white flex items-center gap-1">
258270
<DollarSign class="w-4 h-4" />
259-
{formatCurrency(opp.amount)}
271+
{formatCurrency(opp.amount || 0)}
260272
</p>
261273
<span class="inline-flex px-2 py-1 text-xs rounded-full {getStatusColor(opp.stage)}">
262274
{opp.stage.replace('_', ' ')}

src/routes/(app)/app/contacts/[contactId]/edit/+page.svelte

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,29 @@
66
import { User, Mail, Phone, Building, MapPin, FileText, Star, Save, X, ArrowLeft } from '@lucide/svelte';
77
import { validatePhoneNumber } from '$lib/utils/phone.js';
88
9-
export let data;
9+
/** @type {{ data: import('./$types').PageData }} */
10+
let { data } = $props();
1011
11-
let contact = data.contact;
12+
const { contact } = data;
1213
let account = data.account;
1314
let isPrimary = data.isPrimary;
1415
let role = data.role;
1516
16-
let firstName = contact.firstName;
17-
let lastName = contact.lastName;
18-
let email = contact.email || '';
19-
let phone = contact.phone || '';
20-
let title = contact.title || '';
21-
let department = contact.department || '';
22-
let street = contact.street || '';
23-
let city = contact.city || '';
24-
let state = contact.state || '';
25-
let postalCode = contact.postalCode || '';
26-
let country = contact.country || '';
27-
let description = contact.description || '';
28-
let submitting = false;
29-
let errorMsg = '';
30-
let phoneError = '';
17+
let firstName = $state(contact?.firstName || '');
18+
let lastName = $state(contact?.lastName || '');
19+
let email = $state(contact?.email || '');
20+
let phone = $state(contact?.phone || '');
21+
let title = $state(contact?.title || '');
22+
let department = $state(contact?.department || '');
23+
let street = $state(contact?.street || '');
24+
let city = $state(contact?.city || '');
25+
let stateField = $state(contact?.state || ''); // Renamed to avoid conflict with Svelte's $state
26+
let postalCode = $state(contact?.postalCode || '');
27+
let country = $state(contact?.country || '');
28+
let description = $state(contact?.description || '');
29+
let submitting = $state(false);
30+
let errorMsg = $state('');
31+
let phoneError = $state('');
3132
3233
// Validate phone number on input
3334
function validatePhone() {
@@ -44,6 +45,7 @@
4445
}
4546
}
4647
48+
/** @param {Event} e */
4749
async function handleSubmit(e) {
4850
e.preventDefault();
4951
submitting = true;
@@ -57,7 +59,7 @@
5759
formData.append('department', department);
5860
formData.append('street', street);
5961
formData.append('city', city);
60-
formData.append('state', state);
62+
formData.append('state', stateField);
6163
formData.append('postalCode', postalCode);
6264
formData.append('country', country);
6365
formData.append('description', description);
@@ -69,7 +71,7 @@
6971
});
7072
if (res.ok) {
7173
await invalidateAll();
72-
goto(`/app/contacts/${contact.id}`);
74+
goto(`/app/contacts/${contact?.id}`);
7375
} else {
7476
const data = await res.json();
7577
errorMsg = data?.message || 'Failed to update contact.';
@@ -84,7 +86,7 @@
8486
<div class="mb-8">
8587
<div class="flex items-center gap-4 mb-4">
8688
<button
87-
onclick={() => goto(`/app/contacts/${contact.id}`)}
89+
onclick={() => goto(`/app/contacts/${contact?.id}`)}
8890
class="p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
8991
>
9092
<ArrowLeft class="w-5 h-5" />
@@ -256,7 +258,7 @@
256258
<input
257259
id="state"
258260
class="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400"
259-
bind:value={state}
261+
bind:value={stateField}
260262
placeholder="CA"
261263
/>
262264
</div>
@@ -367,7 +369,7 @@
367369
<div class="flex justify-end gap-3 pt-6 border-t border-gray-200 dark:border-gray-700">
368370
<button
369371
type="button"
370-
onclick={() => goto(`/app/contacts/${contact.id}`)}
372+
onclick={() => goto(`/app/contacts/${contact?.id}`)}
371373
class="px-6 py-3 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 focus:ring-2 focus:ring-gray-500 dark:focus:ring-gray-400 transition-colors"
372374
>
373375
Cancel

src/routes/(app)/app/contacts/new/+page.server.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export async function load({ locals, url }) {
1717

1818
// Get accounts for the account dropdown (if no specific accountId is provided)
1919
const accountId = url.searchParams.get('accountId');
20+
/** @type {Array<{id: string, name: string, organizationId: string}>} */
2021
let accounts = [];
2122

2223
if (!accountId) {
@@ -68,8 +69,8 @@ export const actions = {
6869
}
6970

7071
const data = await request.formData();
71-
const firstName = data.get('firstName')?.toString().trim();
72-
const lastName = data.get('lastName')?.toString().trim();
72+
const firstName = data.get('firstName')?.toString().trim() || '';
73+
const lastName = data.get('lastName')?.toString().trim() || '';
7374
const email = data.get('email')?.toString().trim();
7475
const phone = data.get('phone')?.toString().trim();
7576
const title = data.get('title')?.toString().trim();
@@ -246,7 +247,7 @@ export const actions = {
246247
description: `Created contact: ${firstName} ${lastName}${accountId ? ` and linked to account` : ''}`,
247248
newValues: { contact, accountRelationship: accountId ? { accountId, role, isPrimary } : null },
248249
userId: locals.user.id,
249-
organizationId: validatedOrganizationId || null
250+
organizationId: locals.org.id
250251
}
251252
});
252253

src/routes/(app)/app/contacts/new/+page.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
role: form?.values?.role || ''
3434
});
3535
36+
/** @type {Record<string, string>} */
3637
const errors = $derived(form?.errors || {});
3738
3839
// Find the selected account for display
@@ -42,7 +43,7 @@
4243
4344
function handleSubmit() {
4445
isSubmitting = true;
45-
return async ({ update }) => {
46+
return async (/** @type {{ update: Function }} */ { update }) => {
4647
await update();
4748
isSubmitting = false;
4849
};

0 commit comments

Comments
 (0)