first after 100

This commit is contained in:
yznahmad 2025-12-04 09:05:38 +03:00
parent b1472a7f72
commit cb0960299d
16 changed files with 56 additions and 25 deletions

View File

@ -8,7 +8,7 @@ import Toast from "~/components/Toast";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Areas Management - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Areas Management - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 2); const user = await requireAuthLevel(request, 2);

View File

@ -5,7 +5,7 @@ import { requireAuthLevel } from "~/utils/auth.server";
import DashboardLayout from "~/components/DashboardLayout"; import DashboardLayout from "~/components/DashboardLayout";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Dashboard - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Dashboard - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 1); const user = await requireAuthLevel(request, 1);
@ -51,7 +51,7 @@ export default function Dashboard() {
Welcome back, {user.name}! Welcome back, {user.name}!
</h2> </h2>
<p className="text-sm sm:text-base text-gray-600"> <p className="text-sm sm:text-base text-gray-600">
Here's what's happening with your phosphat operations today. Here's what's happening with your allhaffer operations today.
</p> </p>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ import Toast from "~/components/Toast";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Dredger Locations Management - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Dredger Locations Management - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 2); const user = await requireAuthLevel(request, 2);

View File

@ -9,7 +9,7 @@ import { useState, useEffect } from "react";
import bcrypt from "bcryptjs"; import bcrypt from "bcryptjs";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Employee Management - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Employee Management - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 2); const user = await requireAuthLevel(request, 2);

View File

@ -8,7 +8,7 @@ import Toast from "~/components/Toast";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Equipment Management - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Equipment Management - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 2); const user = await requireAuthLevel(request, 2);

View File

@ -8,7 +8,7 @@ import Toast from "~/components/Toast";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Foreman Management - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Foreman Management - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 2); const user = await requireAuthLevel(request, 2);

View File

@ -8,7 +8,7 @@ import Toast from "~/components/Toast";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Reclamation Locations Management - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Reclamation Locations Management - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 2); const user = await requireAuthLevel(request, 2);

View File

@ -7,7 +7,7 @@ import ReportSheetViewModal from "~/components/ReportSheetViewModal";
import { useState } from "react"; import { useState } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Report Sheets - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Report Sheets - Alhaffer Report System" }];
interface ReportSheet { interface ReportSheet {
id: string; id: string;

View File

@ -10,7 +10,7 @@ import { useState, useEffect } from "react";
import { manageSheet, removeFromSheet } from "~/utils/sheet.server"; import { manageSheet, removeFromSheet } from "~/utils/sheet.server";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Reports Management - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Reports Management - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 1); // All employees can access reports const user = await requireAuthLevel(request, 1); // All employees can access reports

View File

@ -6,7 +6,7 @@ import DashboardLayout from "~/components/DashboardLayout";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Edit Report - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Edit Report - Alhaffer Report System" }];
export const loader = async ({ request, params }: LoaderFunctionArgs) => { export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 1); const user = await requireAuthLevel(request, 1);
@ -112,6 +112,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
const statsExc = formData.get("statsExc"); const statsExc = formData.get("statsExc");
const statsLoaders = formData.get("statsLoaders"); const statsLoaders = formData.get("statsLoaders");
const statsForeman = formData.get("statsForeman"); const statsForeman = formData.get("statsForeman");
const statsLaborer = formData.get("statsLaborer");
const workersListData = formData.get("workersList"); const workersListData = formData.get("workersList");
const timeSheetData = formData.get("timeSheetData"); const timeSheetData = formData.get("timeSheetData");
const stoppagesData = formData.get("stoppagesData"); const stoppagesData = formData.get("stoppagesData");
@ -149,7 +150,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
let finalNotes = typeof notes === "string" ? notes : ''; let finalNotes = typeof notes === "string" ? notes : '';
if (automaticNotes.length > 0) { if (automaticNotes.length > 0) {
const automaticNotesText = automaticNotes.join(', '); const automaticNotesText = automaticNotes.join(', ');
finalNotes = finalNotes.trim() ? `${automaticNotesText}. ${finalNotes}` : automaticNotesText; finalNotes = finalNotes.trim() ? `${automaticNotesText}.\n${finalNotes}` : automaticNotesText;
} }
await prisma.report.update({ await prisma.report.update({
@ -172,7 +173,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
Exc: parseInt(statsExc as string) || 0, Exc: parseInt(statsExc as string) || 0,
Loaders: parseInt(statsLoaders as string) || 0, Loaders: parseInt(statsLoaders as string) || 0,
Foreman: statsForeman as string || "", Foreman: statsForeman as string || "",
Laborer: workersList.length Laborer: parseInt(statsLaborer as string) || 0
}, },
timeSheet, timeSheet,
stoppages, stoppages,
@ -222,6 +223,7 @@ export default function EditReport() {
report.shiftWorkers?.map((sw: any) => sw.worker.id) || [] report.shiftWorkers?.map((sw: any) => sw.worker.id) || []
); );
const [workerSearchTerm, setWorkerSearchTerm] = useState(''); const [workerSearchTerm, setWorkerSearchTerm] = useState('');
const [isLaborerManuallyEdited, setIsLaborerManuallyEdited] = useState(false);
const [timeSheetEntries, setTimeSheetEntries] = useState<Array<{ const [timeSheetEntries, setTimeSheetEntries] = useState<Array<{
id: string, id: string,
@ -467,6 +469,16 @@ export default function EditReport() {
return true; // All steps are optional for editing return true; // All steps are optional for editing
}; };
// Auto-update laborer count when workers change (only if not manually edited)
useEffect(() => {
if (!isLaborerManuallyEdited) {
setFormData(prev => ({
...prev,
statsLaborer: selectedWorkers.length.toString()
}));
}
}, [selectedWorkers, isLaborerManuallyEdited]);
// Worker selection functions // Worker selection functions
const toggleWorker = (workerId: number) => { const toggleWorker = (workerId: number) => {
setSelectedWorkers(prev => setSelectedWorkers(prev =>
@ -762,10 +774,13 @@ export default function EditReport() {
id="statsLaborer" id="statsLaborer"
name="statsLaborer" name="statsLaborer"
min="0" min="0"
value={selectedWorkers.length} value={formData.statsLaborer}
readOnly onChange={(e) => {
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-100 text-gray-700 cursor-not-allowed" updateFormData('statsLaborer', e.target.value);
title="Auto-calculated based on selected workers" setIsLaborerManuallyEdited(true);
}}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
title="Auto-calculated based on selected workers, but can be edited"
/> />
</div> </div>
</div> </div>
@ -1169,6 +1184,7 @@ export default function EditReport() {
<input type="hidden" name="statsExc" value={formData.statsExc} /> <input type="hidden" name="statsExc" value={formData.statsExc} />
<input type="hidden" name="statsLoaders" value={formData.statsLoaders} /> <input type="hidden" name="statsLoaders" value={formData.statsLoaders} />
<input type="hidden" name="statsForeman" value={formData.statsForeman} /> <input type="hidden" name="statsForeman" value={formData.statsForeman} />
<input type="hidden" name="statsLaborer" value={formData.statsLaborer} />
</> </>
)} )}
{currentStep !== 3 && ( {currentStep !== 3 && (

View File

@ -7,7 +7,7 @@ import { useState, useEffect } from "react";
import { manageSheet } from "~/utils/sheet.server"; import { manageSheet } from "~/utils/sheet.server";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "New Report - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "New Report - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 1); // All employees can create reports const user = await requireAuthLevel(request, 1); // All employees can create reports
@ -144,7 +144,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
if (automaticNotes.length > 0) { if (automaticNotes.length > 0) {
const automaticNotesText = automaticNotes.join(', '); const automaticNotesText = automaticNotes.join(', ');
if (finalNotes.trim()) { if (finalNotes.trim()) {
finalNotes = `${automaticNotesText}. ${finalNotes}`; finalNotes = `${automaticNotesText}.\n${finalNotes}`;
} else { } else {
finalNotes = automaticNotesText; finalNotes = automaticNotesText;
} }
@ -178,7 +178,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
Exc: parseInt(statsExc as string) || 0, Exc: parseInt(statsExc as string) || 0,
Loaders: parseInt(statsLoaders as string) || 0, Loaders: parseInt(statsLoaders as string) || 0,
Foreman: statsForeman as string || "", Foreman: statsForeman as string || "",
Laborer: workersList.length Laborer: parseInt(statsLaborer as string) || 0
}, },
timeSheet, timeSheet,
stoppages, stoppages,
@ -241,6 +241,8 @@ export default function NewReport() {
notes: '' notes: ''
}); });
const [isLaborerManuallyEdited, setIsLaborerManuallyEdited] = useState(false);
const [validationError, setValidationError] = useState<string | null>(null); const [validationError, setValidationError] = useState<string | null>(null);
const [selectedWorkers, setSelectedWorkers] = useState<number[]>([]); const [selectedWorkers, setSelectedWorkers] = useState<number[]>([]);
const [workerSearchTerm, setWorkerSearchTerm] = useState(''); const [workerSearchTerm, setWorkerSearchTerm] = useState('');
@ -641,6 +643,16 @@ export default function NewReport() {
} }
}; };
// Auto-update laborer count when workers change (only if not manually edited)
useEffect(() => {
if (!isLaborerManuallyEdited) {
setFormData(prev => ({
...prev,
statsLaborer: selectedWorkers.length.toString()
}));
}
}, [selectedWorkers, isLaborerManuallyEdited]);
// Worker selection functions // Worker selection functions
const toggleWorker = (workerId: number) => { const toggleWorker = (workerId: number) => {
setSelectedWorkers(prev => setSelectedWorkers(prev =>
@ -909,7 +921,10 @@ export default function NewReport() {
<div><label htmlFor="statsExc" className="block text-sm font-medium text-gray-700 mb-2">Excavators <span className="text-xs text-gray-500">(Auto-calculated)</span></label><input type="number" id="statsExc" name="statsExc" min="0" value={formData.statsExc} readOnly className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-100 text-gray-700 cursor-not-allowed" title="Auto-calculated based on time sheet entries" /></div> <div><label htmlFor="statsExc" className="block text-sm font-medium text-gray-700 mb-2">Excavators <span className="text-xs text-gray-500">(Auto-calculated)</span></label><input type="number" id="statsExc" name="statsExc" min="0" value={formData.statsExc} readOnly className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-100 text-gray-700 cursor-not-allowed" title="Auto-calculated based on time sheet entries" /></div>
<div><label htmlFor="statsLoaders" className="block text-sm font-medium text-gray-700 mb-2">Loaders <span className="text-xs text-gray-500">(Auto-calculated)</span></label><input type="number" id="statsLoaders" name="statsLoaders" min="0" value={formData.statsLoaders} readOnly className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-100 text-gray-700 cursor-not-allowed" title="Auto-calculated based on time sheet entries" /></div> <div><label htmlFor="statsLoaders" className="block text-sm font-medium text-gray-700 mb-2">Loaders <span className="text-xs text-gray-500">(Auto-calculated)</span></label><input type="number" id="statsLoaders" name="statsLoaders" min="0" value={formData.statsLoaders} readOnly className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-100 text-gray-700 cursor-not-allowed" title="Auto-calculated based on time sheet entries" /></div>
<div><label htmlFor="statsForeman" className="block text-sm font-medium text-gray-700 mb-2">Foreman</label><select id="statsForeman" name="statsForeman" value={formData.statsForeman} onChange={(e) => updateFormData('statsForeman', e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"><option value="">Select foreman</option>{foremen.map((foreman) => (<option key={foreman.id} value={foreman.name}>{foreman.name}</option>))}</select></div> <div><label htmlFor="statsForeman" className="block text-sm font-medium text-gray-700 mb-2">Foreman</label><select id="statsForeman" name="statsForeman" value={formData.statsForeman} onChange={(e) => updateFormData('statsForeman', e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"><option value="">Select foreman</option>{foremen.map((foreman) => (<option key={foreman.id} value={foreman.name}>{foreman.name}</option>))}</select></div>
<div><label htmlFor="statsLaborer" className="block text-sm font-medium text-gray-700 mb-2">Laborers <span className="text-xs text-gray-500">({selectedWorkers.length} selected)</span></label><input type="number" id="statsLaborer" name="statsLaborer" min="0" value={selectedWorkers.length} readOnly className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-100 text-gray-700 cursor-not-allowed" title="Auto-calculated based on selected workers" /></div> <div><label htmlFor="statsLaborer" className="block text-sm font-medium text-gray-700 mb-2">Laborers <span className="text-xs text-gray-500">({selectedWorkers.length} selected)</span></label><input type="number" id="statsLaborer" name="statsLaborer" min="0" value={formData.statsLaborer} onChange={(e) => {
updateFormData('statsLaborer', e.target.value);
setIsLaborerManuallyEdited(true);
}} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" title="Auto-calculated based on selected workers, but can be edited" /></div>
</div> </div>
</div> </div>
<div> <div>

View File

@ -3,7 +3,7 @@ import { json, redirect } from "@remix-run/node";
import { Form, Link, useActionData, useSearchParams } from "@remix-run/react"; import { Form, Link, useActionData, useSearchParams } from "@remix-run/react";
import { createUserSession, getUserId, verifyLogin } from "~/utils/auth.server"; import { createUserSession, getUserId, verifyLogin } from "~/utils/auth.server";
export const meta: MetaFunction = () => [{ title: "Sign In - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Sign In - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const userId = await getUserId(request); const userId = await getUserId(request);

View File

@ -4,7 +4,7 @@ import { Form, Link, useActionData } from "@remix-run/react";
import { createUser, createUserSession, getUserId } from "~/utils/auth.server"; import { createUser, createUserSession, getUserId } from "~/utils/auth.server";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Sign Up - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Sign Up - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const userId = await getUserId(request); const userId = await getUserId(request);

View File

@ -6,7 +6,7 @@ import DashboardLayout from "~/components/DashboardLayout";
import { useState } from "react"; import { useState } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
export const meta: MetaFunction = () => [{ title: "Stoppages Analysis - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Stoppages Analysis - Alhaffer Report System" }];
interface StoppageEntry { interface StoppageEntry {
id: string; id: string;

View File

@ -7,7 +7,7 @@ import { useState, useEffect } from "react";
import { prisma } from "~/utils/db.server"; import { prisma } from "~/utils/db.server";
import Toast from "~/components/Toast"; import Toast from "~/components/Toast";
export const meta: MetaFunction = () => [{ title: "Workers - Phosphat Report" }]; export const meta: MetaFunction = () => [{ title: "Workers - Alhaffer Report System" }];
export const loader = async ({ request }: LoaderFunctionArgs) => { export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireAuthLevel(request, 2); // Only supervisors and admins const user = await requireAuthLevel(request, 2); // Only supervisors and admins

Binary file not shown.