Merge pull request #186 from Oloodi/183-web-enable-order-visualization-via-event-entity-vendororders

183 web enable order visualization via event entity vendororders
This commit is contained in:
José Salazar
2025-12-18 10:07:54 -05:00
committed by GitHub
25 changed files with 203 additions and 84 deletions

View File

@@ -3,7 +3,9 @@ mutation CreateActivityLog(
$description: String!, $description: String!,
$activityType: ActivityType!, $activityType: ActivityType!,
$userId: String!, $userId: String!,
$isRead: Boolean $isRead: Boolean,
$iconType: String,
$iconColor: String
) @auth(level: USER) { ) @auth(level: USER) {
activityLog_insert( activityLog_insert(
data: { data: {
@@ -12,6 +14,8 @@ mutation CreateActivityLog(
activityType: $activityType activityType: $activityType
userId: $userId userId: $userId
isRead: $isRead isRead: $isRead
iconType: $iconType
iconColor: $iconColor
} }
) )
} }
@@ -22,7 +26,9 @@ mutation UpdateActivityLog(
$description: String, $description: String,
$activityType: ActivityType, $activityType: ActivityType,
$userId: String, $userId: String,
$isRead: Boolean $isRead: Boolean,
$iconType: String,
$iconColor: String
) @auth(level: USER) { ) @auth(level: USER) {
activityLog_update( activityLog_update(
id: $id, id: $id,
@@ -32,6 +38,8 @@ mutation UpdateActivityLog(
activityType: $activityType activityType: $activityType
userId: $userId userId: $userId
isRead: $isRead isRead: $isRead
iconType: $iconType
iconColor: $iconColor
} }
) )
} }

View File

@@ -6,6 +6,8 @@ query listActivityLog @auth(level: USER) {
activityType activityType
userId userId
isRead isRead
iconType
iconColor
} }
} }
@@ -19,6 +21,8 @@ query getActivityLogById(
activityType activityType
userId userId
isRead isRead
iconType
iconColor
} }
} }
@@ -40,5 +44,7 @@ query filterActivityLog(
activityType activityType
userId userId
isRead isRead
iconType
iconColor
} }
} }

View File

@@ -1,9 +1,13 @@
query listEvents ( query listEvents (
$orderByDate: OrderDirection $orderByDate: OrderDirection
$orderByCreatedDate: OrderDirection,
$limit: Int $limit: Int
) @auth(level: USER) { ) @auth(level: USER) {
events( events(
orderBy: { date: $orderByDate } orderBy: [
{ date: $orderByDate }
{ createdDate: $orderByCreatedDate }
]
limit: $limit limit: $limit
) { ) {
id id
@@ -101,20 +105,21 @@ query filterEvents(
$contractType: ContractType, $contractType: ContractType,
$clientEmail: String $clientEmail: String
) @auth(level: USER) { ) @auth(level: USER) {
events(where: { events(
status: { eq: $status } where: {
businessId: { eq: $businessId } status: { eq: $status }
vendorId: { eq: $vendorId } businessId: { eq: $businessId }
isRecurring: { eq: $isRecurring } vendorId: { eq: $vendorId }
isRapid: { eq: $isRapid } isRecurring: { eq: $isRecurring }
isMultiDay: { eq: $isMultiDay } isRapid: { eq: $isRapid }
recurrenceType: { eq: $recurrenceType } isMultiDay: { eq: $isMultiDay }
date: { eq: $date } recurrenceType: { eq: $recurrenceType }
hub: { eq: $hub } date: { eq: $date }
eventLocation: { eq: $eventLocation } hub: { eq: $hub }
contractType: { eq: $contractType } eventLocation: { eq: $eventLocation }
clientEmail: { eq: $clientEmail } contractType: { eq: $contractType }
}) { clientEmail: { eq: $clientEmail }
}) {
id id
eventName eventName
status status

View File

@@ -15,7 +15,9 @@ type ActivityLog @table(name: "activity_logs") {
activityType: ActivityType! activityType: ActivityType!
userId: String! # user_id (FK lógica a User.id) userId: String! # user_id (FK lógica a User.id)
isRead: Boolean @default(expr: "false") isRead: Boolean @default(expr: "false")
iconType: String
iconColor: String
createdDate: Timestamp @default(expr: "request.time") createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time") updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid") createdBy: String @default(expr: "auth.uid")
} }

View File

@@ -7,6 +7,10 @@ enum EventStatus {
CONFIRMED CONFIRMED
COMPLETED COMPLETED
CANCELED CANCELED
PARTIAL
PARTIAL_STAFFED
FULLY_STAFFED
EVENT_CREATED
} }
enum RecurrenceType { enum RecurrenceType {

View File

@@ -312,19 +312,39 @@ const dataconnectEntityConfig = {
}, },
Sector:{ Sector:{
list: 'listSector',
get: 'getSectorById',
create: 'createSector',
update: 'updateSector',
delete: 'deleteSector',
filter: 'filterSector'
}, },
Partner:{ Partner:{
list: 'listPartner',
get: 'getPartnerById',
create: 'createPartner',
update: 'updatePartner',
delete: 'deletePartner',
filter: 'filterPartner'
}, },
Order:{ Order:{
list: 'listOrder',
get: 'getOrderById',
create: 'createOrder',
update: 'updateOrder',
delete: 'deleteOrder',
filter: 'filterOrder'
}, },
Shift:{ Shift:{
list: 'listShift',
get: 'getShiftById',
create: 'createShift',
update: 'updateShift',
delete: 'deleteShift',
filter: 'filterShift'
} }
}; };
@@ -405,7 +425,7 @@ Object.entries(dataconnectEntityConfig).forEach(([entityName, ops]) => {
// list // list
...(ops.list && { ...(ops.list && {
list: async (...args) => { list: async (...args) => {
const fn = dcSdk[ops.list]; /*const fn = dcSdk[ops.list];
if (typeof fn !== 'function') { if (typeof fn !== 'function') {
throw new Error( throw new Error(
`Data Connect operation "${ops.list}" not found for entity "${entityName}".` `Data Connect operation "${ops.list}" not found for entity "${entityName}".`
@@ -446,10 +466,8 @@ Object.entries(dataconnectEntityConfig).forEach(([entityName, ops]) => {
} }
} }
// COMMENT FIX: variables que realmente se mandan a DataConnect
let variables = baseVariables; let variables = baseVariables;
// COMMENT FIX: caso especial para Team, que SÍ tiene orderBy/limit en el query
if (entityName === "Team") { if (entityName === "Team") {
variables = variables || {}; variables = variables || {};
if (sort) { if (sort) {
@@ -464,7 +482,7 @@ Object.entries(dataconnectEntityConfig).forEach(([entityName, ops]) => {
const res = await fn(dataConnect, variables); const res = await fn(dataConnect, variables);
let items = normalizeResultToArray(res); let items = normalizeResultToArray(res);
// COMMENT FIX: para entidades que NO tienen orderBy/limit en el query, // para entidades que NO tienen orderBy/limit en el query,
// aplicamos sort/limit en el front como fallback. // aplicamos sort/limit en el front como fallback.
if (entityName !== "Team" && sort) { if (entityName !== "Team" && sort) {
const desc = sort.startsWith("-"); const desc = sort.startsWith("-");
@@ -495,7 +513,83 @@ Object.entries(dataconnectEntityConfig).forEach(([entityName, ops]) => {
} }
//console.log(items) //console.log(items)
//return items; //return items;
*/
const fn = dcSdk[ops.list];
if (typeof fn !== 'function') {
throw new Error(
`Data Connect operation "${ops.list}" not found for entity "${entityName}".`
);
}
try {
// --- Lógica de parseo de argumentos (se mantiene para compatibilidad) ---
let sort;
let limit;
let baseVariables;
if (args.length === 1) {
const [a0] = args;
if (typeof a0 === "string") sort = a0;
else if (a0 && typeof a0 === "object") baseVariables = a0;
} else if (args.length === 2) {
const [a0, a1] = args;
if (typeof a0 === "string" && typeof a1 === "number") {
sort = a0;
limit = a1;
} else if (a0 && typeof a0 === "object") {
baseVariables = a0;
if (typeof a1 === "string") sort = a1;
}
} else if (args.length >= 3) {
const [a0, a1, a2] = args;
if (a0 && typeof a0 === "object") {
baseVariables = a0;
if (typeof a1 === "string") sort = a1;
if (typeof a2 === "number") limit = a2;
}
}
// --- FIX: Se llama al backend SIN parámetros de orden/límite ---
// Se asume que la mayoría de queries 'list' no aceptan variables, solo se pasan si existen.
//const res = await fn(dataConnect, baseVariables);
const res = await fn(dataConnect, baseVariables);
let items = normalizeResultToArray(res);
// --- FIX: SE RESTAURA el ordenamiento y límite en el CLIENTE ---
// Esto es necesario hasta que el backend pueda manejar estos parámetros.
if (sort) {
const isDesc = sort.startsWith("-");
const field = isDesc ? sort.slice(1) : sort;
items = items.slice().sort((a, b) => {
const av = a?.[field];
const bv = b?.[field];
if (av == null && bv == null) return 0;
if (av == null) return 1;
if (bv == null) return -1;
const da = new Date(av);
const db = new Date(bv);
if (!isNaN(da) && !isNaN(db)) {
return isDesc ? db - da : da - db;
}
if (av < bv) return isDesc ? 1 : -1;
if (av > bv) return isDesc ? -1 : 1;
return 0;
});
}
if (typeof limit === "number") {
items = items.slice(0, limit);
}
return items.map(toSnake); return items.map(toSnake);
} catch (err) {
console.error('list staff failed', err);
throw err;
}
}, },
}), }),

View File

@@ -312,17 +312,17 @@ export default function EventForm({ event, onSubmit, isSubmitting, currentUser }
e.preventDefault(); e.preventDefault();
let status; let status;
if (isDraft) { if (isDraft) {
status = "Draft"; status = "DRAFT";
} else { } else {
switch (formData.order_type) { switch (formData.order_type) {
case "rapid": case "rapid":
status = "Active"; // Rapid requests are active immediately upon submission status = "ACTIVE"; // Rapid requests are active immediately upon submission
break; break;
case "one_time": case "one_time":
case "recurring": case "recurring":
case "permanent": case "permanent":
default: // In case of an unexpected order_type, default to Pending default: // In case of an unexpected order_type, default to Pending
status = "Pending"; // These types typically need approval/processing status = "PENDING"; // These types typically need approval/processing
break; break;
} }
} }

View File

@@ -543,8 +543,8 @@ export default function EventFormWizard({ event, onSubmit, onRapidSubmit, isSubm
}; };
const handleSubmit = (isDraft = false) => { const handleSubmit = (isDraft = false) => {
const status = isDraft ? "Draft" : const status = isDraft ? "DRAFT" :
formData.order_type === "rapid" ? "Active" : "Pending"; formData.order_type === "rapid" ? "ACTIVE" : "PENDING";
const totalRequested = formData.shifts.reduce((sum, shift) => { const totalRequested = formData.shifts.reduce((sum, shift) => {
return sum + shift.roles.reduce((roleSum, role) => roleSum + (parseInt(role.count) || 0), 0); return sum + shift.roles.reduce((roleSum, role) => roleSum + (parseInt(role.count) || 0), 0);

View File

@@ -81,7 +81,7 @@ export default function QuickReorderModal({ event, open, onOpenChange }) {
date: format(date, 'yyyy-MM-dd'), date: format(date, 'yyyy-MM-dd'),
requested: formData.requested, requested: formData.requested,
notes: formData.notes, notes: formData.notes,
status: "Pending", status: "PENDING",
assigned: 0, assigned: 0,
assigned_staff: [] assigned_staff: []
})); }));

View File

@@ -130,7 +130,7 @@ export function NotificationEngine() {
user.id, user.id,
'⏰ Shift Reminder', '⏰ Shift Reminder',
`Reminder: Your shift at ${event.event_name} is tomorrow`, `Reminder: Your shift at ${event.event_name} is tomorrow`,
'event_updated', 'EVENT_UPDATED',
event.id event.id
); );
@@ -177,7 +177,7 @@ export function NotificationEngine() {
clientUser.id, clientUser.id,
'📅 Upcoming Event', '📅 Upcoming Event',
`Your event "${event.event_name}" is in 3 days`, `Your event "${event.event_name}" is in 3 days`,
'event_created', 'EVENT_CREATED',
event.id event.id
); );
@@ -199,7 +199,7 @@ export function NotificationEngine() {
useEffect(() => { useEffect(() => {
const notifyVendorsNewLeads = async () => { const notifyVendorsNewLeads = async () => {
const newEvents = events.filter(e => const newEvents = events.filter(e =>
e.status === 'Draft' || e.status === 'Pending' e.status === 'DRAFT' || e.status === 'PENDING'
); );
const vendorUsers = users.filter(u => u.role === 'vendor'); const vendorUsers = users.filter(u => u.role === 'vendor');
@@ -212,7 +212,7 @@ export function NotificationEngine() {
// Check if already notified // Check if already notified
const recentNotifs = await base44.entities.ActivityLog.filter({ const recentNotifs = await base44.entities.ActivityLog.filter({
userId: vendor.id, userId: vendor.id,
activityType: 'event_created', activityType: 'EVENT_CREATED',
related_entity_id: event.id, related_entity_id: event.id,
}); });
@@ -222,7 +222,7 @@ export function NotificationEngine() {
vendor.id, vendor.id,
'🎯 New Lead Available', '🎯 New Lead Available',
`New opportunity: ${event.event_name} needs ${event.requested || 0} staff`, `New opportunity: ${event.event_name} needs ${event.requested || 0} staff`,
'event_created', 'EVENT_CREATED',
event.id event.id
); );

View File

@@ -1,16 +1,16 @@
// Utility to calculate order status based on current state // Utility to calculate order status based on current state
export function calculateOrderStatus(event) { export function calculateOrderStatus(event) {
// Check explicit statuses first // Check explicit statuses first
if (event.status === "Canceled" || event.status === "Cancelled") { if (event.status === "CANCELLED" || event.status === "CANCELLED") {
return "Canceled"; return "CANCELLED";
} }
if (event.status === "Draft") { if (event.status === "DRAFT") {
return "Draft"; return "DRAFT";
} }
if (event.status === "Completed") { if (event.status === "COMPLETED") {
return "Completed"; return "COMPLETED";
} }
// Calculate status based on staffing // Calculate status based on staffing
@@ -18,20 +18,20 @@ export function calculateOrderStatus(event) {
const assigned = event.assigned_staff?.length || 0; const assigned = event.assigned_staff?.length || 0;
if (requested === 0) { if (requested === 0) {
return "Draft"; // No staff requested yet return "DRAFT"; // No staff requested yet
} }
if (assigned === 0) { if (assigned === 0) {
return "Pending"; // Awaiting assignment return "PENDING"; // Awaiting assignment
} }
if (assigned < requested) { if (assigned < requested) {
return "Partial"; // Partially staffed return "PARTIAL"; // Partially staffed
} }
if (assigned >= requested) { if (assigned >= requested) {
return "Confirmed"; // Fully staffed return "CONFIRMED"; // Fully staffed
} }
return "Pending"; return "PENDING";
} }

View File

@@ -130,7 +130,7 @@ Return a concise summary.`,
const orderData = { const orderData = {
event_name: `RAPID: ${detectedOrder.count} ${detectedOrder.role}${detectedOrder.count > 1 ? 's' : ''}`, event_name: `RAPID: ${detectedOrder.count} ${detectedOrder.role}${detectedOrder.count > 1 ? 's' : ''}`,
is_rapid: true, is_rapid: true,
status: "Pending", status: "PENDING",
business_name: detectedOrder.business_name, business_name: detectedOrder.business_name,
hub: detectedOrder.hub, hub: detectedOrder.hub,
event_location: detectedOrder.location, event_location: detectedOrder.location,

View File

@@ -32,11 +32,11 @@ export default function WorkerConfirmationCard({ assignment, event }) {
const getStatusColor = () => { const getStatusColor = () => {
switch (assignment.assignment_status) { switch (assignment.assignment_status) {
case "Confirmed": case "CONFIRMED":
return "bg-green-100 text-green-700 border-green-300"; return "bg-green-100 text-green-700 border-green-300";
case "Cancelled": case "Cancelled":
return "bg-red-100 text-red-700 border-red-300"; return "bg-red-100 text-red-700 border-red-300";
case "Pending": case "PENDING":
return "bg-yellow-100 text-yellow-700 border-yellow-300"; return "bg-yellow-100 text-yellow-700 border-yellow-300";
default: default:
return "bg-slate-100 text-slate-700 border-slate-300"; return "bg-slate-100 text-slate-700 border-slate-300";

View File

@@ -23,7 +23,7 @@ export default function TaskDetailModal({ task, open, onClose }) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [comment, setComment] = useState(""); const [comment, setComment] = useState("");
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
const [status, setStatus] = useState(task?.status || "pending"); const [status, setStatus] = useState(task?.status || "PENDING");
const [activeTab, setActiveTab] = useState("updates"); const [activeTab, setActiveTab] = useState("updates");
const [emailNotification, setEmailNotification] = useState(false); const [emailNotification, setEmailNotification] = useState(false);
const fileInputRef = useRef(null); const fileInputRef = useRef(null);
@@ -244,10 +244,10 @@ export default function TaskDetailModal({ task, open, onClose }) {
}; };
const statusOptions = [ const statusOptions = [
{ value: "pending", label: "Pending", icon: Clock, color: "bg-slate-100 text-slate-700 border-slate-300" }, { value: "PENDING", label: "Pending", icon: Clock, color: "bg-slate-100 text-slate-700 border-slate-300" },
{ value: "in_progress", label: "In Progress", icon: Zap, color: "bg-blue-100 text-blue-700 border-blue-300" }, { value: "in_progress", label: "In Progress", icon: Zap, color: "bg-blue-100 text-blue-700 border-blue-300" },
{ value: "on_hold", label: "On Hold", icon: PauseCircle, color: "bg-orange-100 text-orange-700 border-orange-300" }, { value: "on_hold", label: "On Hold", icon: PauseCircle, color: "bg-orange-100 text-orange-700 border-orange-300" },
{ value: "completed", label: "Completed", icon: CheckCircle, color: "bg-green-100 text-green-700 border-green-300" }, { value: "COMPLETED", label: "Completed", icon: CheckCircle, color: "bg-green-100 text-green-700 border-green-300" },
]; ];
return ( return (

View File

@@ -105,11 +105,11 @@ async function createNotification(title, description, variant) {
if (variant === "destructive" || title.includes("Failed") || title.includes("Error")) { if (variant === "destructive" || title.includes("Failed") || title.includes("Error")) {
icon_type = "alert"; icon_type = "alert";
icon_color = "red"; icon_color = "red";
activity_type = "event_updated"; activity_type = "EVENT_UPDATED";
} else if (title.includes("Success") || title.includes("✅") || title.includes("Saved") || title.includes("Created")) { } else if (title.includes("Success") || title.includes("✅") || title.includes("Saved") || title.includes("Created")) {
icon_type = "check"; icon_type = "check";
icon_color = "green"; icon_color = "green";
activity_type = "event_created"; activity_type = "EVENT_CREATED";
} else if (title.includes("Invoice") || title.includes("Payment")) { } else if (title.includes("Invoice") || title.includes("Payment")) {
icon_type = "invoice"; icon_type = "invoice";
icon_color = "purple"; icon_color = "purple";
@@ -117,7 +117,7 @@ async function createNotification(title, description, variant) {
} else if (title.includes("Event") || title.includes("Order")) { } else if (title.includes("Event") || title.includes("Order")) {
icon_type = "calendar"; icon_type = "calendar";
icon_color = "blue"; icon_color = "blue";
activity_type = "event_created"; activity_type = "EVENT_CREATED";
} else if (title.includes("User") || title.includes("Staff") || title.includes("Member")) { } else if (title.includes("User") || title.includes("Staff") || title.includes("Member")) {
icon_type = "user"; icon_type = "user";
icon_color = "green"; icon_color = "green";

View File

@@ -171,9 +171,9 @@ export default function Events() {
if (totalAssigned >= totalRequested && totalRequested > 0) { if (totalAssigned >= totalRequested && totalRequested > 0) {
newStatus = 'Fully Staffed'; newStatus = 'Fully Staffed';
} else if (totalAssigned > 0 && totalAssigned < totalRequested) { } else if (totalAssigned > 0 && totalAssigned < totalRequested) {
newStatus = 'Partial Staffed'; newStatus = 'PARTIAL_STAFFED';
} else if (totalAssigned === 0) { } else if (totalAssigned === 0) {
newStatus = 'Pending'; newStatus = 'PENDING';
} }
await base44.entities.Event.update(event.id, { await base44.entities.Event.update(event.id, {
@@ -212,7 +212,7 @@ export default function Events() {
if (updatedAssignedStaff.length >= totalRequested && totalRequested > 0) { if (updatedAssignedStaff.length >= totalRequested && totalRequested > 0) {
newStatus = 'Fully Staffed'; newStatus = 'Fully Staffed';
} else if (updatedAssignedStaff.length > 0 && updatedAssignedStaff.length < totalRequested) { } else if (updatedAssignedStaff.length > 0 && updatedAssignedStaff.length < totalRequested) {
newStatus = 'Partial Staffed'; newStatus = 'PARTIAL_STAFFED';
} }
await updateEventMutation.mutateAsync({ await updateEventMutation.mutateAsync({
@@ -238,11 +238,11 @@ export default function Events() {
let newStatus = event.status; let newStatus = event.status;
if (updatedAssignedStaff.length >= totalRequested && totalRequested > 0) { if (updatedAssignedStaff.length >= totalRequested && totalRequested > 0) {
newStatus = 'Fully Staffed'; newStatus = 'FULLY_STAFFED';
} else if (updatedAssignedStaff.length > 0 && updatedAssignedStaff.length < totalRequested) { } else if (updatedAssignedStaff.length > 0 && updatedAssignedStaff.length < totalRequested) {
newStatus = 'Partial Staffed'; newStatus = 'PARTIAL_STAFFED';
} else if (updatedAssignedStaff.length === 0) { } else if (updatedAssignedStaff.length === 0) {
newStatus = 'Pending'; newStatus = 'PENDING';
} }
await updateEventMutation.mutateAsync({ await updateEventMutation.mutateAsync({
@@ -259,11 +259,11 @@ export default function Events() {
const getStatusCounts = () => { const getStatusCounts = () => {
const total = events.length; const total = events.length;
const active = events.filter(e => e.status === "Active").length; const active = events.filter(e => e.status === "ACTIVE").length;
const pending = events.filter(e => e.status === "Pending").length; const pending = events.filter(e => e.status === "PENDING").length;
const partialStaffed = events.filter(e => e.status === "Partial Staffed").length; const partialStaffed = events.filter(e => e.status === "PARTIAL_STAFFED").length;
const fullyStaffed = events.filter(e => e.status === "Fully Staffed").length; const fullyStaffed = events.filter(e => e.status === "FULLY_STAFFED").length;
const completed = events.filter(e => e.status === "Completed").length; const completed = events.filter(e => e.status === "COMPLETED").length;
return { return {
active: { count: active, percentage: total ? Math.round((active / total) * 100) : 0 }, active: { count: active, percentage: total ? Math.round((active / total) * 100) : 0 },

View File

@@ -53,7 +53,7 @@ export default function InviteVendor() {
primary_contact_email: data.primary_contact_email, primary_contact_email: data.primary_contact_email,
vendor_admin_fee: parseFloat(data.vendor_admin_fee), vendor_admin_fee: parseFloat(data.vendor_admin_fee),
invited_by: user?.email || "admin", invited_by: user?.email || "admin",
invite_status: "pending", invite_status: "PENDING",
invite_sent_date: new Date().toISOString(), invite_sent_date: new Date().toISOString(),
notes: data.notes notes: data.notes
}); });

View File

@@ -260,7 +260,7 @@ export default function Invoices() {
<Badge className="ml-2 bg-yellow-400 text-yellow-900 hover:bg-yellow-400 border-0 font-bold">{getStatusCount("all")}</Badge> <Badge className="ml-2 bg-yellow-400 text-yellow-900 hover:bg-yellow-400 border-0 font-bold">{getStatusCount("all")}</Badge>
</TabsTrigger> </TabsTrigger>
<TabsTrigger <TabsTrigger
value="pending" value="PENDING"
className="data-[state=active]:bg-blue-600 data-[state=active]:text-white bg-white text-slate-700 hover:bg-slate-50 transition-all rounded-md px-3 py-2" className="data-[state=active]:bg-blue-600 data-[state=active]:text-white bg-white text-slate-700 hover:bg-slate-50 transition-all rounded-md px-3 py-2"
> >
<Clock className="w-4 h-4 mr-2" /> <Clock className="w-4 h-4 mr-2" />

View File

@@ -34,7 +34,7 @@ export default function Onboarding() {
queryKey: ['team-invite', inviteCode], queryKey: ['team-invite', inviteCode],
queryFn: async () => { queryFn: async () => {
const allInvites = await base44.entities.TeamMemberInvite.list(); const allInvites = await base44.entities.TeamMemberInvite.list();
const foundInvite = allInvites.find(inv => inv.invite_code === inviteCode && inv.invite_status === 'pending'); const foundInvite = allInvites.find(inv => inv.invite_code === inviteCode && inv.invite_status === 'PENDING');
if (foundInvite) { if (foundInvite) {
// Pre-fill form with invite data // Pre-fill form with invite data
@@ -109,7 +109,7 @@ export default function Onboarding() {
} }
// Check if invite was already accepted // Check if invite was already accepted
if (invite.invite_status !== 'pending') { if (invite.invite_status !== 'PENDING') {
throw new Error("This invitation has already been used. Please contact your team administrator for a new invitation."); throw new Error("This invitation has already been used. Please contact your team administrator for a new invitation.");
} }

View File

@@ -227,7 +227,7 @@ Return a concise summary.`,
const orderData = { const orderData = {
event_name: `RAPID: ${exactCount} ${detectedOrder.role}${exactCount > 1 ? 's' : ''}`, event_name: `RAPID: ${exactCount} ${detectedOrder.role}${exactCount > 1 ? 's' : ''}`,
is_rapid: true, is_rapid: true,
status: "Pending", status: "PENDING",
business_name: detectedOrder.business_name, business_name: detectedOrder.business_name,
hub: detectedOrder.hub, hub: detectedOrder.hub,
event_location: detectedOrder.location, event_location: detectedOrder.location,

View File

@@ -560,10 +560,10 @@ export default function SmartVendorOnboarding() {
w9_document: formData.w9_url, w9_document: formData.w9_url,
coi_document: formData.coi_url, coi_document: formData.coi_url,
insurance_certificate: formData.coi_url, insurance_certificate: formData.coi_url,
approval_status: "pending", approval_status: "PENDING",
is_active: false, is_active: false,
// Updated contract note to reflect review not upload // Updated contract note to reflect review not upload
notes: `Total Employees: ${formData.total_employees}, Software: ${formData.software_name || formData.software_type}, SOS: ${formData.sos_url ? 'Verified' : 'Pending'}, Contract: ${formData.contract_acknowledged ? 'Acknowledged' : 'Not Acknowledged'}. Contract Review Notes: ${formData.contract_review_notes}. NDA Signed: ${formData.nda_acknowledged ? 'Yes' : 'No'}` notes: `Total Employees: ${formData.total_employees}, Software: ${formData.software_name || formData.software_type}, SOS: ${formData.sos_url ? 'Verified' : 'PENDING'}, Contract: ${formData.contract_acknowledged ? 'Acknowledged' : 'Not Acknowledged'}. Contract Review Notes: ${formData.contract_review_notes}. NDA Signed: ${formData.nda_acknowledged ? 'Yes' : 'No'}`
}); });
// Create rate proposals with location-specific rates // Create rate proposals with location-specific rates

View File

@@ -133,7 +133,7 @@ export default function TeamDetails() {
full_name: data.full_name, full_name: data.full_name,
role: data.role, role: data.role,
invited_by: user?.email || user?.full_name, invited_by: user?.email || user?.full_name,
invite_status: "pending", invite_status: "PENDING",
invited_date: new Date().toISOString(), invited_date: new Date().toISOString(),
expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() // 7 days expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() // 7 days
}); });

View File

@@ -264,7 +264,7 @@ export default function Teams() {
hub: firstHub, hub: firstHub,
department: firstDept, department: firstDept,
invited_by: user?.email || user?.full_name, invited_by: user?.email || user?.full_name,
invite_status: "pending", invite_status: "PENDING",
invited_date: new Date().toISOString(), invited_date: new Date().toISOString(),
expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
}); });
@@ -331,7 +331,7 @@ export default function Teams() {
hub: data.hub || "", hub: data.hub || "",
department: data.department || "", department: data.department || "",
invited_by: user?.email || user?.full_name, invited_by: user?.email || user?.full_name,
invite_status: "pending", invite_status: "PENDING",
invited_date: new Date().toISOString(), invited_date: new Date().toISOString(),
expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
}); });

View File

@@ -313,7 +313,7 @@ export default function VendorDashboard() {
return <Badge className="bg-green-600 text-white text-xs border-0 px-2.5 py-0.5 font-bold flex items-center gap-1"><CheckCircle className="w-3 h-3" />Fully Staffed</Badge>; return <Badge className="bg-green-600 text-white text-xs border-0 px-2.5 py-0.5 font-bold flex items-center gap-1"><CheckCircle className="w-3 h-3" />Fully Staffed</Badge>;
} }
if (order.status === "Pending") { if (order.status === "PENDING") {
return <Badge className="bg-orange-500 text-white text-xs border-0 px-2.5 py-0.5 font-bold flex items-center gap-1"><Clock className="w-3 h-3" />Pending</Badge>; return <Badge className="bg-orange-500 text-white text-xs border-0 px-2.5 py-0.5 font-bold flex items-center gap-1"><Clock className="w-3 h-3" />Pending</Badge>;
} }

View File

@@ -155,11 +155,11 @@ export default function VendorOrders() {
let newStatus = event.status; let newStatus = event.status;
if (totalAssigned >= totalRequested && totalRequested > 0) { if (totalAssigned >= totalRequested && totalRequested > 0) {
newStatus = 'Fully Staffed'; newStatus = 'FULLY_STAFFED';
} else if (totalAssigned > 0 && totalAssigned < totalRequested) { } else if (totalAssigned > 0 && totalAssigned < totalRequested) {
newStatus = 'Partial Staffed'; newStatus = 'PARTIAL_STAFFED';
} else if (totalAssigned === 0) { } else if (totalAssigned === 0) {
newStatus = 'Pending'; newStatus = 'PENDING';
} }
await base44.entities.Event.update(event.id, { await base44.entities.Event.update(event.id, {
@@ -217,7 +217,7 @@ export default function VendorOrders() {
return filtered; return filtered;
}, [eventsWithConflicts, searchTerm, activeTab]); }, [eventsWithConflicts, searchTerm, activeTab]);
const getAssignmentStatus = (event) => {debugger; const getAssignmentStatus = (event) => {
const totalRequested = event.shifts?.reduce((accShift, shift) => { const totalRequested = event.shifts?.reduce((accShift, shift) => {
return accShift + (shift.roles?.reduce((accRole, role) => accRole + (role.count || 0), 0) || 0); return accShift + (shift.roles?.reduce((accRole, role) => accRole + (role.count || 0), 0) || 0);
}, 0) || 0; }, 0) || 0;