123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- import { Prisma, PrismaClient, ProgressStatus } from '@prisma/client';
- import { SearchFilter } from '../../utils/SearchFilter';
- import { createLog, updateLog } from '../../utils/LogActivity';
- import { HttpException } from '../../utils/HttpException';
- import salesHospitalRepository from '../../repository/sales/HospitalRepository';
- import ProvinceRepository from '../../repository/admin/ProvinceRepository';
- import CityRepository from '../../repository/admin/CityRepository';
- import HospitalRepository from '../../repository/admin/HospitalRepository';
- import { CustomRequest } from '../../types/token/CustomRequest';
- import { HospitalRequestDTO } from '../../types/sales/hospital/HospitalDTO';
- import path from 'path';
- import fs from 'fs/promises';
- import sharp from 'sharp';
- // import { storeCategoryLinkService, updateCategoryLinkService } from '../admin/CategoryLinkService';
- const prisma = new PrismaClient();
- export interface PaginationParams {
- page: number;
- limit: number;
- search?: string;
- sortBy: string;
- orderBy: 'asc' | 'desc';
- province?: string;
- city?: string;
- type?: string;
- ownership?: string;
- progress_status?: string;
- }
- export const getAllHospitalByAreaService = async (params: PaginationParams, req: CustomRequest) => {
- const { page, limit, search, sortBy, orderBy, province, city, type, ownership, progress_status } = params;
- const skip = (page - 1) * limit;
- const userAreas = await prisma.userArea.findMany({
- where: { user_id: req.tokenData.sub },
- select: { province_id: true },
- });
- const provinceIds = userAreas.map((ua) => ua.province_id);
- const where: any = {
- ...SearchFilter(search, ['name', 'province.id', 'city.id', 'type', 'ownership']),
- province_id: { in: provinceIds },
- ...(province ? { province_id: province } : {}),
- ...(city ? { city_id: city } : {}),
- ...(type ? { type } : {}),
- ...(ownership ? { ownership } : {}),
- ...(progress_status ? { progress_status } : {}),
- deletedAt: null,
- };
- const [hospitals, total] = await Promise.all([
- salesHospitalRepository.findAll({ skip, take: limit, where, orderBy: { [sortBy]: orderBy } }),
- salesHospitalRepository.countAll(where),
- ]);
- return { hospitals, total };
- };
- export const storeHospitalService = async (validateData: HospitalRequestDTO, req: CustomRequest) => {
- const creatorId = req.tokenData.sub;
- const province = await ProvinceRepository.findById(validateData.province_id!);
- if (!province) throw new HttpException('Province not found', 404);
- const userArea = await prisma.userArea.findFirst({
- where: { user_id: creatorId, province_id: validateData.province_id },
- });
- if (!userArea) throw new HttpException('You are not allowed to add hospital to this province', 403);
- const city = await CityRepository.findById(validateData.city_id!);
- if (!city) throw new HttpException('City not found', 404);
- const existingHospital = await prisma.hospital.findFirst({
- where: {
- name: validateData.name,
- city_id: validateData.city_id,
- deletedAt: null,
- },
- });
- if (existingHospital) throw new HttpException('Hospital with same name in this city already exists', 400);
- let imagePath: string | null = null;
- if (req.file) {
- const ext = '.jpg';
- const filename = `${Date.now()}-${Math.round(Math.random() * 1e9)}${ext}`;
- const fullPath = path.join(__dirname, '../../../storage/img', filename);
- let outputBuffer = await sharp(req.file.buffer)
- .resize({ width: 1280, withoutEnlargement: true })
- .toFormat('jpeg', { quality: 80 })
- .toBuffer();
- // Pastikan size <= 500 KB
- let quality = 80;
- while (outputBuffer.length > 500 * 1024 && quality > 20) {
- quality -= 10;
- outputBuffer = await sharp(req.file.buffer)
- .resize({ width: 1280, withoutEnlargement: true })
- .toFormat('jpeg', { quality })
- .toBuffer();
- }
- await fs.writeFile(fullPath, outputBuffer);
- imagePath = `/storage/img/${filename}`;
- }
- let { latitude = null, longitude = null, gmaps_url: gmapsUrl = null } = validateData;
- if (gmapsUrl) {
- if (gmapsUrl.includes('www.google.com/maps')) {
- const match = gmapsUrl.match(/@(-?\d+\.\d+),(-?\d+\.\d+)/);
- if (match) {
- latitude = parseFloat(match[1]);
- longitude = parseFloat(match[2]);
- } else {
- throw new HttpException('Unable to extract coordinates from gmaps_url', 400);
- }
- } else if (!gmapsUrl.includes('maps.app.goo.gl')) {
- throw new HttpException('gmaps_url must be a valid Google Maps URL', 400);
- }
- } else if (latitude === null || longitude === null) {
- throw new HttpException('Either gmaps_url or coordinates must be provided', 400);
- }
- const payload = {
- name: validateData.name!,
- hospital_code: validateData.hospital_code,
- type: validateData.type,
- ownership: validateData.ownership,
- address: validateData.address,
- contact: validateData.contact,
- note: validateData.note,
- image: imagePath,
- latitude,
- longitude,
- gmaps_url: gmapsUrl,
- progress_status: ProgressStatus.cari_data,
- province: {
- connect: { id: validateData.province_id },
- },
- city: {
- connect: { id: validateData.city_id },
- },
- user: {
- connect: { id: creatorId },
- },
- };
- const data = await salesHospitalRepository.create(payload);
- // if (validateData.tags?.length) {
- // await storeCategoryLinkService(
- // validateData.tags,
- // 'hospital_notes',
- // data.id,
- // req
- // );
- // }
- await createLog(req, data);
- };
- export const updateHospitalService = async (validateData: Partial<HospitalRequestDTO>, id: string, req: CustomRequest) => {
- const hospital = await HospitalRepository.findById(id);
- if (!hospital) throw new HttpException('Hospital data not found', 404);
- const isOwner = hospital.created_by === req.tokenData.sub;
- // Cek apakah user memiliki akses ke provinsi hospital
- const userArea = await prisma.userArea.findFirst({
- where: {
- user_id: req.tokenData.sub,
- province_id: hospital.province.id,
- },
- });
- const hasProvinceAccess = !!userArea;
- if (!isOwner && !hasProvinceAccess) {
- throw new HttpException('You are not authorized to access this hospital', 403);
- }
- if (validateData.province_id) {
- const province = await ProvinceRepository.findById(validateData.province_id);
- if (!province) throw new HttpException('Province not found', 404);
- }
- if (validateData.city_id) {
- const city = await CityRepository.findById(validateData.city_id);
- if (!city) throw new HttpException('City not found', 404);
- }
- if (
- validateData.progress_status &&
- !Object.values(ProgressStatus).includes(validateData.progress_status as ProgressStatus)
- ) {
- throw new HttpException(
- `Invalid progress_status. Allowed values are: ${Object.values(ProgressStatus).join(', ')}`,
- 422
- );
- }
- if (validateData.name && validateData.city_id) {
- const existingHospital = await prisma.hospital.findFirst({
- where: {
- name: validateData.name,
- city_id: validateData.city_id,
- deletedAt: null,
- NOT: { id },
- },
- });
- if (existingHospital) {
- throw new HttpException('Hospital with same name in this city already exists', 400);
- }
- }
- let imagePath = hospital.image;
- if (req.file) {
- const ext = '.jpg';
- const filename = `${Date.now()}-${Math.round(Math.random() * 1e9)}${ext}`;
- const fullPath = path.join(__dirname, '../../../storage/img', filename);
- let outputBuffer = await sharp(req.file.buffer)
- .resize({ width: 1280, withoutEnlargement: true })
- .toFormat('jpeg', { quality: 80 })
- .toBuffer();
- // Pastikan size <= 500 KB
- let quality = 80;
- while (outputBuffer.length > 500 * 1024 && quality > 20) {
- quality -= 10;
- outputBuffer = await sharp(req.file.buffer)
- .resize({ width: 1280, withoutEnlargement: true })
- .toFormat('jpeg', { quality })
- .toBuffer();
- }
- await fs.writeFile(fullPath, outputBuffer);
- imagePath = `/storage/img/${filename}`;
- }
- let latitude = hospital.latitude;
- let longitude = hospital.longitude;
- let gmapsUrl = hospital.gmaps_url;
- if (
- validateData.latitude !== undefined &&
- validateData.longitude !== undefined &&
- validateData.latitude !== null &&
- validateData.longitude !== null
- ) {
- latitude = validateData.latitude;
- longitude = validateData.longitude;
- gmapsUrl = validateData.gmaps_url || gmapsUrl;
- } else if (validateData.gmaps_url?.trim()) {
- gmapsUrl = validateData.gmaps_url;
- if (gmapsUrl.includes('www.google.com/maps')) {
- const match = gmapsUrl.match(/@(-?\d+\.\d+),(-?\d+\.\d+)/);
- if (match) {
- latitude = parseFloat(match[1]);
- longitude = parseFloat(match[2]);
- } else {
- throw new HttpException('Unable to extract coordinates from gmaps_url', 400);
- }
- } else if (!gmapsUrl.includes('maps.app.goo.gl')) {
- throw new HttpException('gmaps_url must be a valid Google Maps URL', 400);
- }
- }
- const payload: Prisma.HospitalUpdateInput = {
- ...(validateData.name && { name: validateData.name }),
- ...(validateData.hospital_code && { hospital_code: validateData.hospital_code }),
- ...(validateData.type && { type: validateData.type }),
- ...(validateData.ownership && { ownership: validateData.ownership }),
- ...(validateData.address && { address: validateData.address }),
- ...(validateData.contact && { contact: validateData.contact }),
- ...(validateData.note !== undefined && { note: validateData.note }),
- ...(validateData.progress_status && { progress_status: validateData.progress_status }),
- ...(imagePath && { image: imagePath }),
- ...(latitude !== undefined && { latitude }),
- ...(longitude !== undefined && { longitude }),
- ...(gmapsUrl !== undefined && { gmaps_url: gmapsUrl }),
- ...(validateData.province_id && {
- province: {
- connect: { id: validateData.province_id }
- }
- }),
- ...(validateData.city_id && {
- city: {
- connect: { id: validateData.city_id }
- }
- }),
- };
- const data = await salesHospitalRepository.update(id, payload);
- // if (validateData.tags?.length) {
- // await updateCategoryLinkService(
- // validateData.tags,
- // 'hospital_notes',
- // id,
- // req
- // );
- // }
- await updateLog(req, data);
- };
- export const showHospitalService = async (id: string, req: CustomRequest) => {
- const hospital = await salesHospitalRepository.findById(id);
- if (!hospital) throw new HttpException('Data hospital not found', 404);
- const isOwner = hospital.created_by === req.tokenData.sub;
- // Cek apakah user memiliki akses ke provinsi hospital
- const userArea = await prisma.userArea.findFirst({
- where: {
- user_id: req.tokenData.sub,
- province_id: hospital.province.id,
- },
- });
- const hasProvinceAccess = !!userArea;
- if (!isOwner && !hasProvinceAccess) {
- throw new HttpException('You are not authorized to access this hospital', 403);
- }
- return { hospital };
- };
|