- Move menu manager button to toolbar next to Save Menu for visibility - Implement server-side photo upload for menu items - Strip base64 data URLs from save payload to reduce size - Add scheduled tasks, quick tasks, ratings, and task categories APIs - Add vertical support and brand color features Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
122 lines
4.4 KiB
Text
122 lines
4.4 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfcontent type="application/json; charset=utf-8">
|
|
<cfscript>
|
|
/**
|
|
* Create an admin rating for a worker on a completed task
|
|
*
|
|
* POST: {
|
|
* TaskID: 123,
|
|
* AdminUserID: 456,
|
|
* onTime: true/false,
|
|
* completedScope: true/false,
|
|
* requiredFollowup: true/false,
|
|
* continueAllow: true/false
|
|
* }
|
|
*/
|
|
|
|
function readJsonBody() {
|
|
var raw = getHttpRequestData().content;
|
|
if (isNull(raw) || len(trim(raw)) == 0) return {};
|
|
try {
|
|
var data = deserializeJSON(raw);
|
|
return isStruct(data) ? data : {};
|
|
} catch (any e) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function generateToken() {
|
|
return lcase(replace(createUUID(), "-", "", "all"));
|
|
}
|
|
|
|
try {
|
|
data = readJsonBody();
|
|
taskID = val(structKeyExists(data, "TaskID") ? data.TaskID : 0);
|
|
adminUserID = val(structKeyExists(data, "AdminUserID") ? data.AdminUserID : 0);
|
|
|
|
if (taskID == 0) {
|
|
writeOutput(serializeJSON({ "OK": false, "ERROR": "missing_task", "MESSAGE": "TaskID is required." }));
|
|
abort;
|
|
}
|
|
|
|
if (adminUserID == 0) {
|
|
writeOutput(serializeJSON({ "OK": false, "ERROR": "missing_admin", "MESSAGE": "AdminUserID is required." }));
|
|
abort;
|
|
}
|
|
|
|
// Verify task exists and is completed
|
|
qTask = queryExecute("
|
|
SELECT t.TaskID, t.TaskClaimedByUserID, t.TaskCompletedOn, t.TaskBusinessID
|
|
FROM Tasks t
|
|
WHERE t.TaskID = :taskID
|
|
", { taskID: taskID });
|
|
|
|
if (qTask.recordCount == 0) {
|
|
writeOutput(serializeJSON({ "OK": false, "ERROR": "not_found", "MESSAGE": "Task not found." }));
|
|
abort;
|
|
}
|
|
|
|
if (len(trim(qTask.TaskCompletedOn)) == 0) {
|
|
writeOutput(serializeJSON({ "OK": false, "ERROR": "not_completed", "MESSAGE": "Task has not been completed yet." }));
|
|
abort;
|
|
}
|
|
|
|
workerUserID = qTask.TaskClaimedByUserID;
|
|
if (workerUserID == 0) {
|
|
writeOutput(serializeJSON({ "OK": false, "ERROR": "no_worker", "MESSAGE": "No worker assigned to this task." }));
|
|
abort;
|
|
}
|
|
|
|
// Check if admin rating already exists for this task
|
|
qExisting = queryExecute("
|
|
SELECT TaskRatingID FROM TaskRatings
|
|
WHERE TaskRatingTaskID = :taskID
|
|
AND TaskRatingDirection = 'admin_rates_worker'
|
|
LIMIT 1
|
|
", { taskID: taskID });
|
|
|
|
if (qExisting.recordCount > 0) {
|
|
writeOutput(serializeJSON({ "OK": false, "ERROR": "already_rated", "MESSAGE": "This task has already been rated by an admin." }));
|
|
abort;
|
|
}
|
|
|
|
// Insert the admin rating (completed immediately since admin submits directly)
|
|
token = generateToken();
|
|
queryExecute("
|
|
INSERT INTO TaskRatings (
|
|
TaskRatingTaskID, TaskRatingByUserID, TaskRatingForUserID, TaskRatingDirection,
|
|
TaskRatingOnTime, TaskRatingCompletedScope, TaskRatingRequiredFollowup, TaskRatingContinueAllow,
|
|
TaskRatingAccessToken, TaskRatingExpiresOn, TaskRatingCompletedOn
|
|
) VALUES (
|
|
:taskID, :adminUserID, :workerUserID, 'admin_rates_worker',
|
|
:onTime, :completedScope, :requiredFollowup, :continueAllow,
|
|
:token, DATE_ADD(NOW(), INTERVAL 24 HOUR), NOW()
|
|
)
|
|
", {
|
|
taskID: taskID,
|
|
adminUserID: adminUserID,
|
|
workerUserID: workerUserID,
|
|
onTime: { value: structKeyExists(data,"onTime") ? (data.onTime ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"onTime") },
|
|
completedScope: { value: structKeyExists(data,"completedScope") ? (data.completedScope ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"completedScope") },
|
|
requiredFollowup: { value: structKeyExists(data,"requiredFollowup") ? (data.requiredFollowup ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"requiredFollowup") },
|
|
continueAllow: { value: structKeyExists(data,"continueAllow") ? (data.continueAllow ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"continueAllow") },
|
|
token: token
|
|
});
|
|
|
|
ratingID = queryExecute("SELECT LAST_INSERT_ID() AS id", {}).id;
|
|
|
|
writeOutput(serializeJSON({
|
|
"OK": true,
|
|
"MESSAGE": "Rating submitted successfully.",
|
|
"RatingID": ratingID
|
|
}));
|
|
|
|
} catch (any e) {
|
|
writeOutput(serializeJSON({
|
|
"OK": false,
|
|
"ERROR": "server_error",
|
|
"MESSAGE": "Error creating rating",
|
|
"DETAIL": e.message
|
|
}));
|
|
}
|
|
</cfscript>
|