ca-grow-ops-manager/backend/src/controllers/walkthrough.controller.ts
fullsizemalt 2036011fdc
Some checks are pending
Deploy to Production / deploy (push) Waiting to run
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run
fix: handle stale tokens by checking user existence before creating walkthrough
2025-12-12 23:10:34 -08:00

323 lines
9.4 KiB
TypeScript

import { FastifyRequest, FastifyReply } from 'fastify';
interface CreateWalkthroughBody {
date?: string;
}
interface AddReservoirCheckBody {
tankName: string;
tankType: 'VEG' | 'FLOWER';
levelPercent: number;
status: 'OK' | 'LOW' | 'CRITICAL';
photoUrl?: string;
notes?: string;
}
interface AddIrrigationCheckBody {
zoneName: string;
drippersTotal: number;
drippersWorking: number;
drippersFailed?: string[];
waterFlow: boolean;
nutrientsMixed: boolean;
scheduleActive: boolean;
photoUrl?: string;
issues?: string;
}
interface AddPlantHealthCheckBody {
zoneName: string;
healthStatus: 'GOOD' | 'FAIR' | 'NEEDS_ATTENTION';
pestsObserved: boolean;
pestType?: string;
waterAccess: 'OK' | 'ISSUES';
foodAccess: 'OK' | 'ISSUES';
flaggedForAttention?: boolean;
issuePhotoUrl?: string;
referencePhotoUrl?: string;
notes?: string;
}
/**
* Start a new daily walkthrough
*/
export const createWalkthrough = async (request: FastifyRequest, reply: FastifyReply) => {
const { date } = (request.body as CreateWalkthroughBody) || {};
const userId = (request.user as any)?.userId;
if (!userId) {
return reply.code(401).send({ message: 'Unauthorized' });
}
// Verify user exists (handle stale tokens after re-seed)
const userExists = await request.server.prisma.user.findUnique({
where: { id: userId }
});
if (!userExists) {
return reply.code(401).send({ message: 'User not found. Please log in again.' });
}
try {
const walkthrough = await request.server.prisma.dailyWalkthrough.create({
data: {
date: date ? new Date(date) : new Date(),
completedBy: userId,
status: 'IN_PROGRESS',
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
role: true,
},
},
reservoirChecks: true,
irrigationChecks: true,
plantHealthChecks: true,
},
});
return reply.code(201).send(walkthrough);
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to create walkthrough' });
}
};
/**
* Get all walkthroughs (with optional filters)
*/
export const getWalkthroughs = async (request: FastifyRequest, reply: FastifyReply) => {
const { status, startDate, endDate, userId } = request.query as any;
try {
const walkthroughs = await request.server.prisma.dailyWalkthrough.findMany({
where: {
...(status && { status }),
...(userId && { completedBy: userId }),
...(startDate && endDate && {
date: {
gte: new Date(startDate),
lte: new Date(endDate),
},
}),
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
role: true,
},
},
reservoirChecks: true,
irrigationChecks: true,
plantHealthChecks: true,
},
orderBy: {
date: 'desc',
},
});
return walkthroughs;
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to fetch walkthroughs' });
}
};
/**
* Get today's walkthrough (if exists)
*/
export const getTodaysWalkthrough = async (request: FastifyRequest, reply: FastifyReply) => {
try {
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const walkthrough = await request.server.prisma.dailyWalkthrough.findFirst({
where: {
date: {
gte: today,
lt: tomorrow,
},
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
role: true,
},
},
reservoirChecks: true,
irrigationChecks: true,
plantHealthChecks: true,
},
orderBy: {
startTime: 'desc',
},
});
if (!walkthrough) {
return reply.code(404).send({ message: 'No walkthrough found for today' });
}
return walkthrough;
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to fetch today\'s walkthrough' });
}
};
/**
* Get a single walkthrough by ID
*/
export const getWalkthrough = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as { id: string };
try {
const walkthrough = await request.server.prisma.dailyWalkthrough.findUnique({
where: { id },
include: {
user: {
select: {
id: true,
name: true,
email: true,
role: true,
},
},
reservoirChecks: true,
irrigationChecks: true,
plantHealthChecks: true,
},
});
if (!walkthrough) {
return reply.code(404).send({ message: 'Walkthrough not found' });
}
return walkthrough;
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to fetch walkthrough' });
}
};
/**
* Complete a walkthrough
*/
export const completeWalkthrough = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as { id: string };
try {
const walkthrough = await request.server.prisma.dailyWalkthrough.update({
where: { id },
data: {
status: 'COMPLETED',
endTime: new Date(),
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
role: true,
},
},
reservoirChecks: true,
irrigationChecks: true,
plantHealthChecks: true,
},
});
return walkthrough;
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to complete walkthrough' });
}
};
/**
* Add a reservoir check to a walkthrough
*/
export const addReservoirCheck = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as { id: string };
const checkData = request.body as AddReservoirCheckBody;
try {
const check = await request.server.prisma.reservoirCheck.create({
data: {
walkthroughId: id,
...checkData,
},
});
return reply.code(201).send(check);
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to add reservoir check' });
}
};
/**
* Add an irrigation check to a walkthrough
*/
export const addIrrigationCheck = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as { id: string };
const checkData = request.body as AddIrrigationCheckBody;
try {
const check = await request.server.prisma.irrigationCheck.create({
data: {
walkthroughId: id,
zoneName: checkData.zoneName,
drippersTotal: checkData.drippersTotal,
drippersWorking: checkData.drippersWorking,
drippersFailed: checkData.drippersFailed ? JSON.stringify(checkData.drippersFailed) : null,
waterFlow: checkData.waterFlow,
nutrientsMixed: checkData.nutrientsMixed,
scheduleActive: checkData.scheduleActive,
photoUrl: checkData.photoUrl,
issues: checkData.issues,
},
});
return reply.code(201).send(check);
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to add irrigation check' });
}
};
/**
* Add a plant health check to a walkthrough
*/
export const addPlantHealthCheck = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as { id: string };
const checkData = request.body as AddPlantHealthCheckBody;
try {
const check = await request.server.prisma.plantHealthCheck.create({
data: {
walkthroughId: id,
...checkData,
flaggedForAttention: checkData.flaggedForAttention || false,
},
});
return reply.code(201).send(check);
} catch (error) {
request.log.error(error);
return reply.code(500).send({ message: 'Failed to add plant health check' });
}
};