Skip to content

Commit 85cd8e4

Browse files
authored
Merge pull request #57 from RGBOARD/queue_order
Queue Item Order Update
2 parents b975051 + dce1d12 commit 85cd8e4

8 files changed

Lines changed: 135 additions & 15 deletions

File tree

app.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,14 @@ def get_queue_paginated():
262262

263263
except Exception as e:
264264
return jsonify({'error': str(e)}), 500
265-
265+
266+
@app.route("/queue_item/<int:queue_id>/order", methods=['PUT'])
267+
@jwt_required()
268+
def update_item_order(queue_id):
269+
data = request.get_json()
270+
new_order = data.get('new_order')
271+
handler = QueueItem(email=get_jwt_identity())
272+
return handler.update_item_order(queue_id=queue_id, new_order=new_order)
266273

267274
@app.route("/queue_item", methods=['GET', 'POST'])
268275
def handleQueueItem():

controller/queue_item.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,23 @@ def updateQueueItemById(self, queue_id, data):
7474
result = self.make_json_one(queue_item)
7575
return result
7676

77+
def update_item_order(self, queue_id, new_order):
78+
79+
if queue_id is None:
80+
return jsonify(error="No id provided."), 400
81+
if self.email is not None:
82+
if self.user.is_admin():
83+
queue_dao = QueueItemDAO()
84+
response = queue_dao.update_item_order(queue_id, new_order)
85+
if response == 0:
86+
return jsonify("Order updated"), 200
87+
else:
88+
return jsonify(error="Couldn't update Order"), 500
89+
else:
90+
return jsonify(error="Unauthorized."),
91+
92+
return jsonify(error="Unauthorized. No token."), 401
93+
7794
def deleteQueueItemById(self, queue_id):
7895
dao = QueueItemDAO()
7996
queue_item = dao.deleteQueueItemById(queue_id)

model/queue_item.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,29 @@ def updateQueueItemById(self, queue_id, data):
7979
cursor.execute(query, (queue_id,))
8080
result = cursor.fetchone()
8181
cursor.close()
82-
return result
82+
return
83+
84+
def update_item_order(self, queue_id: int, new_order: int):
85+
status = 1
86+
query = None
87+
cursor = self.conn.cursor()
88+
old_order = self.getQueueItemById(queue_id)[5]
89+
if new_order < old_order:
90+
query = "UPDATE queue_item SET display_order = display_order + 1 WHERE display_order >= ? AND display_order < ?"
91+
elif new_order > old_order:
92+
query = "UPDATE queue_item SET display_order = display_order - 1 WHERE display_order <= ? AND display_order > ?"
93+
try:
94+
if query:
95+
cursor.execute(query, (new_order, old_order))
96+
shift = "UPDATE queue_item SET display_order = ? WHERE queue_id = ?"
97+
cursor.execute(shift,(new_order, queue_id))
98+
self.conn.commit()
99+
status = 0
100+
except sqlite3.Error:
101+
status = 1
102+
finally:
103+
cursor.close()
104+
return status
83105

84106
def deleteQueueItemById(self, queue_id):
85107

@@ -96,6 +118,16 @@ def deleteQueueItemById(self, queue_id):
96118
status = 0
97119
try:
98120
cursor.execute(query, (queue_id,))
121+
122+
query = "SELECT queue_id FROM queue_item ORDER BY display_order ASC"
123+
124+
cursor.execute(query)
125+
items = cursor.fetchall()
126+
127+
for index, item in enumerate(items):
128+
query = "UPDATE queue_item SET display_order = ? WHERE queue_id = ?"
129+
cursor.execute(query, (index + 1, item[0]))
130+
99131
self.conn.commit()
100132
except sqlite3.Error:
101133
status = 1
@@ -122,10 +154,10 @@ def getScheduledDesigns(self):
122154
d.updated_at
123155
FROM queue_item q
124156
JOIN design d ON q.design_id = d.design_id
125-
WHERE d.is_approved = 1
126-
ORDER BY
127-
CASE WHEN q.scheduled = 1 THEN q.start_time ELSE '9999-12-31 23:59:59' END ASC,
128-
q.display_order ASC;
157+
WHERE d.is_approved = 1 AND q.scheduled = 1
158+
AND strftime('%Y-%m-%d %H:%M:%S', q.start_time) <= strftime('%Y-%m-%d %H:%M:%S', 'now')
159+
AND strftime('%Y-%m-%d %H:%M:%S', q.end_time) >= strftime('%Y-%m-%d %H:%M:%S', 'now')
160+
ORDER BY q.display_order ASC;
129161
"""
130162
try:
131163
cursor.execute(query)
@@ -153,9 +185,7 @@ def get_all_items_paginated(self, page, page_size):
153185
d.is_approved
154186
FROM queue_item q
155187
JOIN design d ON q.design_id = d.design_id
156-
ORDER BY
157-
CASE WHEN q.scheduled = 1 THEN q.start_time ELSE '9999-12-31 23:59:59' END ASC,
158-
q.display_order ASC
188+
ORDER BY q.display_order ASC
159189
LIMIT ? OFFSET ?;
160190
"""
161191
cursor.execute(query, (page_size, offset))

utilities/create.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sqlite3
22

3-
con = sqlite3.connect('data.db')
3+
con = sqlite3.connect('../data.db')
44
cur = con.cursor()
55

66
cur.execute("""

view/src/auth/AuthProvider.jsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,19 @@ export const AuthProvider = ({ children }) => {
202202
};
203203
}
204204
};
205+
// Update order of queue item
206+
const updateorder = async (queueId, newOrder) => {
207+
try {
208+
await axios.put(`/queue_item/${queueId}/order`, { new_order: newOrder });
209+
return { success: true };
210+
} catch (error) {
211+
console.error('Order update error:', error);
212+
return {
213+
success: false,
214+
error: error.response?.data?.error || 'Failed to update admin status'
215+
};
216+
}
217+
};
205218

206219
const deletequeueitem = async (queueId) => {
207220
try {
@@ -236,6 +249,7 @@ export const AuthProvider = ({ children }) => {
236249
toggleadmin,
237250
getpageimages,
238251
toggleapproval,
252+
updateorder,
239253
deletequeueitem,
240254
logout,
241255
hasRole,

view/src/pages/QueueAdminPage.jsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ function QueueAdminPage() {
1212
const [items, setItems] = useState([]);
1313
const [page, setPage] = useState(1);
1414
const [pages, setPages] = useState(1);
15-
const { getpageimages, deletequeueitem, toggleapproval } = useAuth();
15+
const [total, setTotal] = useState(0);
16+
const { getpageimages, deletequeueitem, toggleapproval, updateorder } = useAuth();
1617

1718
const [modalState, setModalState] = useState({
1819
isOpen: false,
@@ -54,6 +55,7 @@ function QueueAdminPage() {
5455
setItems(transformed);
5556
setPage(data.page);
5657
setPages(data.pages);
58+
setTotal(data.total)
5759
} catch (err) {
5860
console.error("Failed to load queue images:", err);
5961
}
@@ -72,6 +74,15 @@ function QueueAdminPage() {
7274
}
7375
};
7476

77+
const handleOrderUpdate = async (queueId, newOrder) => {
78+
try {
79+
await updateorder(queueId, newOrder);
80+
fetchImages(page);
81+
} catch (err) {
82+
console.error("Order update failed:", err);
83+
}
84+
};
85+
7586
const handleToggleApproval = async (designId, isApproved) => {
7687
try {
7788
await toggleapproval(designId, !isApproved);
@@ -122,7 +133,24 @@ function QueueAdminPage() {
122133
<strong>Scheduled at:</strong> { formatDateTime(item?.start_time) || 'Unscheduled'}
123134
</div>
124135
<div className="text-base">
125-
<strong>Order:</strong> {item.display_order}
136+
<strong>Order:</strong> {" "}
137+
<select
138+
className="order-select"
139+
value={item.display_order}
140+
onChange={(e) => {
141+
const newPos = Number(e.target.value);
142+
showConfirm(
143+
`Move item to position ${newPos}?`,
144+
() => handleOrderUpdate(item.queue_id, newPos)
145+
);
146+
}}
147+
>
148+
{Array.from({ length: total }).map((_, idx) => (
149+
<option key={idx + 1} value={idx + 1}>
150+
{idx + 1}
151+
</option>
152+
))}
153+
</select>
126154
</div>
127155
</div>
128156
<div className="user-buttons">

view/src/pages/UploadPage.jsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,9 @@ function UploadPage() {
208208
// Otherwise, compute the next available slot.
209209
const now = new Date();
210210
const nextAvailableStart = new Date(now.getTime() + 60 * 1000); // 1 minute from now
211-
const defaultDurationSeconds = 60; // default duration (60 seconds)
212-
const nextAvailableEnd = new Date(nextAvailableStart.getTime() + defaultDurationSeconds * 1000);
211+
// const defaultDurationSeconds = 60; // default duration (60 seconds)
212+
const oneDayMs = 24 * 60 * 60 * 1000; // If not given a schedule items will stay one day in the active queue
213+
const nextAvailableEnd = new Date(nextAvailableStart.getTime() + oneDayMs);
213214
finalStart = nextAvailableStart.toISOString().substring(0, 19);
214215
finalEnd = nextAvailableEnd.toISOString().substring(0, 19);
215216
}
@@ -231,7 +232,7 @@ function UploadPage() {
231232
showAlert("Image successfully added to queue.");
232233
}
233234
} catch (error) {
234-
showAlert("Error adding image to queue.");
235+
showAlert("Error adding image to queue.", error);
235236
}
236237
};
237238

view/src/pages/styles/QueueAdmin.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
2+
.order-select {
3+
appearance: none;
4+
-webkit-appearance: none;
5+
padding-right: 2rem;
6+
padding-left: .5rem;
7+
min-height: 28px;
8+
border: 1px solid black;
9+
border-radius: 6px;
10+
background-color: #fff;
11+
12+
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 8 5' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%23000000'/%3E%3C/svg%3E");
13+
background-repeat: no-repeat;
14+
background-position: right .5rem center;
15+
background-size: 8px 5px;
16+
}
17+
18+
.order-select:focus {
19+
outline: none;
20+
border-color: #2563eb;
21+
box-shadow: 0 0 0 2px rgba(37,99,235,.3);
22+
}
23+
124
.approval-label {
225
font-family: "Pixelify Sans", sans-serif;
326
border-radius: 24px;

0 commit comments

Comments
 (0)