feat: Initialize monorepo structure and comprehensive documentation

This commit establishes the new monorepo architecture for the KROW Workforce platform.

Key changes include:
- Reorganized project into `frontend-web`, `mobile-apps`, `firebase`, `scripts`, and `secrets` directories.
- Updated `Makefile` to support the new monorepo layout and automate Base44 export integration.
- Fixed `scripts/prepare-export.js` for ES module compatibility and global component import resolution.
- Created and updated `CONTRIBUTING.md` for developer onboarding.
- Restructured, renamed, and translated all `docs/` files for clarity and consistency.
- Implemented an interactive internal launchpad with diagram viewing capabilities.
- Configured base Firebase project files (`firebase.json`, security rules).
- Updated `README.md` to reflect the new project structure and documentation overview.
This commit is contained in:
bwnyasse
2025-11-12 12:50:55 -05:00
parent 92fd0118be
commit 554dc9f9e3
203 changed files with 1414 additions and 732 deletions

View File

@@ -0,0 +1,261 @@
import React, { useState } from "react";
import { base44 } from "@/api/base44Client";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useNavigate } from "react-router-dom";
import { createPageUrl } from "@/utils";
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { ArrowLeft, Bell, RefreshCw } from "lucide-react";
import { format } from "date-fns";
import ShiftCard from "../components/events/ShiftCard";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog";
import { useToast } from "@/components/ui/use-toast";
const statusColors = {
Draft: "bg-gray-100 text-gray-800",
Active: "bg-green-100 text-green-800",
Pending: "bg-purple-100 text-purple-800",
Confirmed: "bg-blue-100 text-blue-800",
Completed: "bg-slate-100 text-slate-800",
Canceled: "bg-red-100 text-red-800" // Added Canceled status for completeness
};
// Safe date formatter
const safeFormatDate = (dateString, formatStr) => {
if (!dateString) return "-";
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) return "-";
return format(date, formatStr);
} catch {
return "-";
}
};
export default function EventDetail() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const [showNotifyDialog, setShowNotifyDialog] = useState(false);
const urlParams = new URLSearchParams(window.location.search);
const eventId = urlParams.get('id');
const { toast } = useToast();
const { data: allEvents, isLoading } = useQuery({
queryKey: ['events'],
queryFn: () => base44.entities.Event.list(),
initialData: [],
});
const { data: shifts } = useQuery({
queryKey: ['shifts', eventId],
queryFn: () => base44.entities.Shift.filter({ event_id: eventId }),
initialData: [],
enabled: !!eventId
});
const event = allEvents.find(e => e.id === eventId);
const handleReorder = () => {
if (!event) return; // Should not happen if event is loaded, but for safety
const reorderData = {
event_name: event.event_name,
business_id: event.business_id,
business_name: event.business_name,
hub: event.hub,
event_location: event.event_location,
event_type: event.event_type,
requested: event.requested,
client_name: event.client_name,
client_email: event.client_email,
client_phone: event.client_phone,
client_address: event.client_address,
notes: event.notes,
};
sessionStorage.setItem('reorderData', JSON.stringify(reorderData));
toast({
title: "Reordering Event",
description: `Creating new order based on "${event.event_name}"`,
});
navigate(createPageUrl("CreateEvent") + "?reorder=true");
};
if (isLoading || !event) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full" />
</div>
);
}
return (
<div className="p-4 md:p-8 bg-slate-50 min-h-screen">
<div className="max-w-[1600px] mx-auto">
<div className="flex items-center gap-4 mb-6">
<Button variant="ghost" size="icon" onClick={() => navigate(createPageUrl("Events"))}>
<ArrowLeft className="w-5 h-5" />
</Button>
<h1 className="text-2xl font-bold">{event.event_name}</h1>
<div className="flex items-center gap-2 ml-auto">
{(event.status === "Completed" || event.status === "Canceled") && (
<Button
onClick={handleReorder}
className="bg-green-600 hover:bg-green-700 text-white"
>
<RefreshCw className="w-4 h-4 mr-2" />
Reorder
</Button>
)}
<Bell className="w-5 h-5" />
<div className="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center text-white font-bold">
M
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<Card className="border-slate-200">
<CardHeader className="bg-gradient-to-br from-blue-50 to-white border-b border-slate-100">
<CardTitle className="text-base">Order Details</CardTitle>
</CardHeader>
<CardContent className="p-6 space-y-4">
<div>
<p className="text-xs text-slate-500">PO number</p>
<p className="font-medium">{event.po_number || event.po || "#RC-36559419"}</p>
</div>
<div>
<p className="text-xs text-slate-500">Data</p>
<p className="font-medium">{safeFormatDate(event.date, "dd.MM.yyyy")}</p>
</div>
<div>
<p className="text-xs text-slate-500">Status</p>
<Badge className={`${statusColors[event.status]} font-medium mt-1`}>
{event.status}
</Badge>
</div>
<div className="flex gap-2 pt-4">
<Button variant="outline" className="flex-1 text-sm">
Edit Order
</Button>
<Button variant="outline" className="flex-1 text-sm text-red-600 hover:text-red-700">
Cancel Order
</Button>
</div>
</CardContent>
</Card>
<Card className="border-slate-200 lg:col-span-2">
<CardHeader className="bg-gradient-to-br from-blue-50 to-white border-b border-slate-100">
<CardTitle className="text-base">Client info</CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="grid grid-cols-2 gap-6">
<div>
<p className="text-xs text-slate-500 mb-1">Client name</p>
<p className="font-medium">{event.client_name || "Legendary"}</p>
</div>
<div>
<p className="text-xs text-slate-500 mb-1">Number</p>
<p className="font-medium">{event.client_phone || "(408) 815-9180"}</p>
</div>
<div className="col-span-2">
<p className="text-xs text-slate-500 mb-1">Address</p>
<p className="font-medium">{event.client_address || event.event_location || "848 E Dash Rd, Ste 264 E San Jose, CA 95122"}</p>
</div>
<div className="col-span-2">
<p className="text-xs text-slate-500 mb-1">Email</p>
<p className="font-medium">{event.client_email || "order@legendarysweetssf.com"}</p>
</div>
</div>
</CardContent>
</Card>
</div>
<Card className="border-slate-200 mb-6">
<CardHeader className="bg-gradient-to-br from-blue-50 to-white border-b border-slate-100">
<CardTitle className="text-base">Event: {event.event_name}</CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="grid grid-cols-2 gap-6 text-sm">
<div>
<p className="text-slate-500">Hub</p>
<p className="font-medium">{event.hub || "Hub Name"}</p>
</div>
<div>
<p className="text-slate-500">Name of Department</p>
<p className="font-medium">Department name</p>
</div>
<div className="col-span-2">
<p className="text-slate-500 mb-2">Order Addons</p>
<div className="flex gap-2">
<Badge variant="outline" className="text-xs">Title</Badge>
<Badge variant="outline" className="text-xs">Travel Time</Badge>
<Badge variant="outline" className="text-xs">Meal Provided</Badge>
</div>
</div>
</div>
</CardContent>
</Card>
<div className="space-y-6">
{shifts.length > 0 ? (
shifts.map((shift, idx) => (
<ShiftCard
key={shift.id}
shift={shift}
onNotifyStaff={() => setShowNotifyDialog(true)}
/>
))
) : (
<ShiftCard
shift={{
shift_name: "Shift 1",
assigned_staff: event.assigned_staff || [],
location: event.event_location,
unpaid_break: 0,
price: 23,
amount: 120
}}
onNotifyStaff={() => setShowNotifyDialog(true)}
/>
)}
</div>
<Dialog open={showNotifyDialog} onOpenChange={setShowNotifyDialog}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<div className="flex items-center justify-center mb-4">
<div className="w-12 h-12 bg-pink-500 rounded-full flex items-center justify-center text-white font-bold text-xl">
L
</div>
</div>
<DialogTitle className="text-center">Notification Name</DialogTitle>
<p className="text-center text-sm text-slate-600">
Order #5 Admin (cancelled/replace) Want to proceed?
</p>
</DialogHeader>
<DialogFooter className="flex gap-3 sm:justify-center">
<Button variant="outline" onClick={() => setShowNotifyDialog(false)} className="flex-1">
Cancel
</Button>
<Button onClick={() => setShowNotifyDialog(false)} className="flex-1 bg-blue-600 hover:bg-blue-700">
Proceed
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
);
}