MIF_E31221222/sigap-website/app/(pages)/(admin)/dashboard/crime-management/crime-overview/action.ts

348 lines
9.8 KiB
TypeScript

'use server';
import { createClient } from '@/app/_utils/supabase/client';
import {
ICrimes,
ICrimesByYearAndMonth,
IDistanceResult,
} from '@/app/_utils/types/crimes';
import { getInjection } from '@/di/container';
import db from '@/prisma/db';
import {
AuthenticationError,
UnauthenticatedError,
} from '@/src/entities/errors/auth';
import { InputParseError } from '@/src/entities/errors/common';
export async function getAvailableYears() {
const instrumentationService = getInjection('IInstrumentationService');
return await instrumentationService.instrumentServerAction(
'Available Years',
{ recordResponse: true },
async () => {
try {
const years = await db.crimes.findMany({
select: {
year: true,
},
distinct: ['year'],
orderBy: {
year: 'asc',
},
});
return years.map((year) => year.year);
} catch (err) {
if (err instanceof InputParseError) {
// return {
// error: err.message,
// };
throw new InputParseError(err.message);
}
if (err instanceof AuthenticationError) {
// return {
// error: 'User not found.',
// };
throw new AuthenticationError(
'There was an error with the credentials. Please try again or contact support.'
);
}
const crashReporterService = getInjection('ICrashReporterService');
crashReporterService.report(err);
// return {
// error:
// 'An error happened. The developers have been notified. Please try again later.',
// };
throw new Error(
'An error happened. The developers have been notified. Please try again later.'
);
}
}
);
}
export async function getCrimeCategories() {
const instrumentationService = getInjection('IInstrumentationService');
return await instrumentationService.instrumentServerAction(
'Crime Categories',
{ recordResponse: true },
async () => {
try {
const categories = await db.crime_categories.findMany({
select: {
id: true,
name: true,
type: true,
},
});
return categories;
} catch (err) {
if (err instanceof InputParseError) {
// return {
// error: err.message,
// };
throw new InputParseError(err.message);
}
if (err instanceof AuthenticationError) {
// return {
// error: 'User not found.',
// };
throw new AuthenticationError(
'There was an error with the credentials. Please try again or contact support.'
);
}
const crashReporterService = getInjection('ICrashReporterService');
crashReporterService.report(err);
// return {
// error:
// 'An error happened. The developers have been notified. Please try again later.',
// };
throw new Error(
'An error happened. The developers have been notified. Please try again later.'
);
}
}
);
}
export async function getCrimes(): Promise<ICrimes[]> {
const instrumentationService = getInjection('IInstrumentationService');
return await instrumentationService.instrumentServerAction(
'District Crime Data',
{ recordResponse: true },
async () => {
try {
const crimes = await db.crimes.findMany({
include: {
districts: {
select: {
name: true,
geographics: {
select: {
address: true,
land_area: true,
year: true,
latitude: true,
longitude: true,
},
},
demographics: {
select: {
number_of_unemployed: true,
population: true,
population_density: true,
year: true,
},
},
},
},
crime_incidents: {
select: {
id: true,
timestamp: true,
description: true,
status: true,
crime_categories: {
select: {
name: true,
type: true,
},
},
locations: {
select: {
address: true,
latitude: true,
longitude: true,
},
},
},
},
},
});
return crimes;
} catch (err) {
if (err instanceof InputParseError) {
// return {
// error: err.message,
// };
throw new InputParseError(err.message);
}
if (err instanceof AuthenticationError) {
// return {
// error: 'User not found.',
// };
throw new AuthenticationError(
'There was an error with the credentials. Please try again or contact support.'
);
}
const crashReporterService = getInjection('ICrashReporterService');
crashReporterService.report(err);
// return {
// error:
// 'An error happened. The developers have been notified. Please try again later.',
// };
throw new Error(
'An error happened. The developers have been notified. Please try again later.'
);
}
}
);
}
export async function getCrimeByYearAndMonth(
year: number,
month: number | 'all'
): Promise<ICrimesByYearAndMonth[]> {
const instrumentationService = getInjection('IInstrumentationService');
return await instrumentationService.instrumentServerAction(
'District Crime Data',
{ recordResponse: true },
async () => {
try {
// Build where clause conditionally based on provided parameters
const whereClause: any = {
year: year, // Always filter by year now since "all" is removed
};
// Only add month to filter if it's not "all"
if (month !== 'all') {
whereClause.month = month;
}
const crimes = await db.crimes.findMany({
where: whereClause,
include: {
districts: {
select: {
name: true,
geographics: {
where: { year }, // Match geographics to selected year
select: {
address: true,
land_area: true,
year: true,
latitude: true,
longitude: true,
},
},
demographics: {
where: { year }, // Match demographics to selected year
select: {
number_of_unemployed: true,
population: true,
population_density: true,
year: true,
},
},
},
},
crime_incidents: {
select: {
id: true,
timestamp: true,
description: true,
status: true,
crime_categories: {
select: {
name: true,
type: true,
},
},
locations: {
select: {
address: true,
latitude: true,
longitude: true,
},
},
},
},
},
});
// Process the data to transform geographics and demographics from array to single object
const processedCrimes = crimes.map((crime) => {
return {
...crime,
districts: {
...crime.districts,
// Convert geographics array to single object matching the year
geographics: crime.districts.geographics[0] || null,
// Convert demographics array to single object matching the year
demographics: crime.districts.demographics[0] || null,
},
};
});
return processedCrimes;
} catch (err) {
if (err instanceof InputParseError) {
throw new InputParseError(err.message);
}
if (err instanceof AuthenticationError) {
throw new AuthenticationError(
'There was an error with the credentials. Please try again or contact support.'
);
}
const crashReporterService = getInjection('ICrashReporterService');
crashReporterService.report(err);
throw new Error(
'An error happened. The developers have been notified. Please try again later.'
);
}
}
);
}
/**
* Calculate distances between units and incidents using PostGIS
* @param unitId Optional unit code to filter by specific unit
* @param districtId Optional district ID to filter by specific district
* @returns Array of distance calculations between units and incidents
*/
export async function calculateDistances(
unitId?: string,
districtId?: string
): Promise<IDistanceResult[]> {
const supabase = createClient();
try {
const { data, error } = await supabase.rpc(
'calculate_unit_incident_distances',
{
unit_id: unitId || null,
district_id: districtId || null,
}
);
if (error) {
console.error('Error calculating distances:', error);
return [];
}
return data || [];
} catch (error) {
console.error('Failed to calculate distances:', error);
return [];
}
}