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:
@@ -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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,10 @@ enum EventStatus {
|
|||||||
CONFIRMED
|
CONFIRMED
|
||||||
COMPLETED
|
COMPLETED
|
||||||
CANCELED
|
CANCELED
|
||||||
|
PARTIAL
|
||||||
|
PARTIAL_STAFFED
|
||||||
|
FULLY_STAFFED
|
||||||
|
EVENT_CREATED
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RecurrenceType {
|
enum RecurrenceType {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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: []
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user