diff --git a/admin.py b/admin.py index 140d6c7..88ac6cc 100644 --- a/admin.py +++ b/admin.py @@ -118,35 +118,20 @@ async def room_wizard(request, order:Order): roomless_orders = {key:value for key,value in orders.items() if not value.room_id and not value.daily} # Result map - result_map = { - 'A':{ - 'type': 'add_existing', - 'to_add': ['b', 'c'] - }, - 'B':{ - 'type': 'new', - 'room_type': 5, - 'room_name': 'generated 1', - 'to_add': ['B', 'a', 'c'] - }, - 'rooms_to_delete': [] - } - result_map = {} - # Get room quotas - room_quota_map = {} + # Check overflows room_quota_overflow = {} for key, value in ITEM_VARIATIONS_MAP['bed_in_room'].items(): + room_quota = get_quota(ITEMS_ID_MAP['bed_in_room'], value) capacity = ROOM_CAPACITY_MAP[key] if key in ROOM_CAPACITY_MAP else 1 - room_quota_map[value] = math.ceil((len(list(filter(lambda y: y.bed_in_room == value, orders.values())))) / capacity) current_quota = len(list(filter(lambda y: y.bed_in_room == value and y.room_owner == True, orders.values()))) - room_quota_overflow[value] = current_quota - (room_quota_map[value] if value in room_quota_map else 0) + room_quota_overflow[value] = current_quota - int(room_quota.size / capacity) if room_quota else 0 # Init rooms to remove result_map["void"] = [] - # Dismember rooms that are over quota + # Remove rooms that are over quota for room_type, overflow_qty in {key:value for key,value in room_quota_overflow.items() if value > 0}.items(): sorted_rooms = sorted(incomplete_orders.values(), key=lambda r: len(r.room_members)) for room_to_remove in sorted_rooms[:overflow_qty]: @@ -215,6 +200,13 @@ async def room_wizard(request, order:Order): tpl = request.app.ctx.tpl.get_template('wizard.html') return html(tpl.render(order=order, all_orders=all_orders, unconfirmed_orders=orders, data=result_map, jsondata=json.dumps(result_map, skipkeys=True, ensure_ascii=False))) +@bp.post('/room/wizard/submit') +async def submin_from_room_wizard(request:Request, order:Order): + '''Will apply changes to the rooms''' + print(request.body) + return text('Not implemented', status=500) + + @bp.get('/propic/remind') async def propic_remind_missing(request, order:Order): await clear_cache(request, order) diff --git a/app.py b/app.py index 51e2088..065116e 100644 --- a/app.py +++ b/app.py @@ -49,7 +49,7 @@ async def handleException(request, exception): statusCode = exception.status_code if hasattr(exception, 'status_code') else 500 try: tpl = app.ctx.tpl.get_template('error.html') - r = html(tpl.render(exception=exception, status_code=statusCode)) + r = html(tpl.render(exception=exception, status_code=statusCode), status=statusCode) except: traceback.print_exc() @@ -98,6 +98,11 @@ async def gen_barcode(request, code): return raw(img.getvalue(), content_type="image/png") +@app.route("/manage/lol") +async def lol(request: Request): + await get_quotas(request) + return text('hi') + @app.route(f"/{ORGANIZER}/{EVENT_NAME}/order///open/") async def redirect_explore(request, code, secret, order: Order, secret2=None): diff --git a/config.example.py b/config.example.py index 247ebec..acde031 100644 --- a/config.example.py +++ b/config.example.py @@ -65,6 +65,9 @@ SPONSORSHIP_COLOR_MAP = { 'normal': (142, 36, 170) } +# Quotes +QUOTES_LIST = [] + # Maps Products metadata name <--> ID ITEMS_ID_MAP = { 'early_bird_ticket': None, diff --git a/ext.py b/ext.py index 2748003..7d0a35c 100644 --- a/ext.py +++ b/ext.py @@ -260,6 +260,32 @@ class Order: to_return = f"{to_return} [ members = {self.room_members} ]" return to_return +@dataclass +class Quota: + def __init__(self, data): + self.items = data['items'] if 'items' in data else [] + self.variations = data['variations'] if 'variations' in data else [] + self.available = data['available'] if 'available' in data else False + self.size = data['size'] if 'size' in data else 0 + self.available_number = data['available_number'] if 'available_number' in data else 0 + + def has_item (self, id: int=-1, variation: int=None): + return id in self.items if not variation else (id in self.items and variation in self.variations) + + def get_left (self): + return self.available_number + + def __repr__(self): + return f'Quota [items={self.items}, variations={self.variations}] [{self.available_number}/{self.size}]' + + def __str__(self): + return f'Quota [items={self.items}, variations={self.variations}] [{self.available_number}/{self.size}]' + +def get_quota(item: int, variation: int = None) -> Quota: + for q in QUOTA_LIST: + if (q.has_item(item, variation)): return q + return None + @dataclass class Quotas: def __init__(self, data): @@ -277,6 +303,21 @@ async def get_quotas(request: Request=None): return Quotas(res) +async def load_item_quotas() -> bool: + global QUOTA_LIST + QUOTA_LIST = [] + logger.info ('[QUOTAS] Loading quotas...') + success = True + try: + res = await pretixClient.get('quotas/?order=id&with_availability=true') + res = res.json() + for quota_data in res['results']: + QUOTA_LIST.append (Quota(quota_data)) + except Exception: + logger.warning(f"[QUOTAS] Error while loading quotas.\n{traceback.format_exc()}") + success = False + return success + async def get_order(request: Request=None): await request.receive_body() return await request.app.ctx.om.get_order(request=request) @@ -340,6 +381,12 @@ class OrderManager: logger.error("[CACHE] Questions were not loading correctly. Aborting filling cache...") return False + # Load quotas + r = await load_item_quotas() + if(not r and check_itemsQuestions): + logger.error("[CACHE] Quotas were not loading correctly. Aborting filling cache...") + return False + cache = {} orderList = [] success = True @@ -375,7 +422,7 @@ class OrderManager: asyncio.create_task(validate_rooms(None, rooms, self)) return success - + async def get_order(self, request=None, code=None, secret=None, nfc_id=None, cached=False): # if it's a nfc id, just retorn it @@ -417,4 +464,4 @@ class OrderManager: if request and secret != res['secret']: raise exceptions.Forbidden("Your session has expired due to a token change. Please check your E-Mail for an updated link!") - return order + return order \ No newline at end of file diff --git a/res/icons/loading.svg b/res/icons/loading.svg deleted file mode 100644 index 8dfd98b..0000000 --- a/res/icons/loading.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/scripts/wizardManager.js b/res/scripts/wizardManager.js index a3396b2..e8866f3 100644 --- a/res/scripts/wizardManager.js +++ b/res/scripts/wizardManager.js @@ -4,6 +4,8 @@ var draggingData = { parentRoomId: 0 } +var allowRedirect = false; + function initObjects (){ draggables = document.querySelectorAll("div.grid.people div.edit-drag"); rooms = document.querySelectorAll("main.container>div.room"); @@ -117,7 +119,7 @@ function getData () { return draggingData; } // This default onbeforeunload event window.onbeforeunload = function(){ - return "Any changes to the rooms will be discarded." + if (!allowRedirect) return "Any changes to the rooms will be discarded." } /* Model managing */ @@ -152,4 +154,44 @@ function onSave (){ } } +/** + * + * @param {Element} element + */ +function submitData (element){ + if (element.ariaDisabled) return; + element.ariaDisabled = true; + element.setAttribute("aria-busy", true); + document.querySelector("#modalClose").setAttribute("disabled", true); + document.querySelector("#modalClose").style.display = 'none'; + // Create request + const xhr = new XMLHttpRequest(); + xhr.open('POST', '/manage/admin/room/wizard/submit', true); + xhr.withCredentials = true; + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.onreadystatechange = function() { + if (xhr.readyState === XMLHttpRequest.DONE) { + let popoverText = document.querySelector("#popover-status-text"); + let popoverStatus = document.querySelector("#popover-status"); + popoverStatus.classList.remove('status-error'); + popoverStatus.classList.remove('status-success'); + if (xhr.status === 200) { + // Handle correct redirect + popoverText.innerText = "Changes applied successfully. Redirecting..." + popoverStatus.classList.add('status-success'); + } else { + // Handle errors + let error = xhr.statusText; + popoverText.innerText = "Could not apply changes: " + error; + console.error('Error submitting data:', error); + popoverStatus.classList.add('status-error'); + } + popoverStatus.showPopover(); + allowRedirect = true; + setTimeout(()=>window.location.assign('/manage/admin'), 3000); + } + }; + xhr.send(JSON.stringify(model)); +} + initObjects (); \ No newline at end of file diff --git a/res/styles/base.css b/res/styles/base.css index 37d9a1a..51bb7d9 100644 --- a/res/styles/base.css +++ b/res/styles/base.css @@ -27,6 +27,14 @@ summary:has(span.status) { 100% { background-position:57.75% 0%; } } +/* Popover */ +*[popover]:popover-open { + border-radius: var(--border-radius); + border: 1px solid #fff; + backdrop-filter: blur(10px); + padding: 1rem; +} + /* Dark theme */ @media only screen and (prefers-color-scheme: dark) { .icon {filter: invert(1);} diff --git a/res/styles/wizard.css b/res/styles/wizard.css index 318166e..d11a5ad 100644 --- a/res/styles/wizard.css +++ b/res/styles/wizard.css @@ -50,6 +50,14 @@ div.room:nth-child(2n) { float: right; } +.status-success { + background-color: #2e9147aa; +} + +.status-error { + background-color: #912e2eaa; +} + /* Dark theme */ @media only screen and (prefers-color-scheme: dark) { div.drag-over { diff --git a/tpl/admin.html b/tpl/admin.html index 7fdd01e..409ab19 100644 --- a/tpl/admin.html +++ b/tpl/admin.html @@ -19,10 +19,10 @@

Rooms

Manage rooms Verify Rooms - Fill Rooms + Fill Rooms

Profiles

- Remind badge upload + Remind badge upload Auto-confirm Rooms
{% include 'components/confirm_action_modal.html' %} diff --git a/tpl/wizard.html b/tpl/wizard.html index 206ace7..9cef6cb 100644 --- a/tpl/wizard.html +++ b/tpl/wizard.html @@ -16,12 +16,13 @@ unconfirmed_orders = all non confirmed rooms orders all_orders = all orders data = assigned rooms --> -

Review rooms

+

Review rooms ?

+
This is the preview page. Re-arrange users by dragging and dropping them in the rooms.
Once finished, scroll down to either 'Confirm' changes or 'Undo' them.

{% for room in data.items() %} {% if room[0] in all_orders %} {%with room_order = unconfirmed_orders[room[0]] %} -
+

{{room_order.room_name if room_order.room_name else room[1]['room_name'] if room[1] and room[1]['room_name'] else ''}}

@@ -65,14 +66,15 @@
- +

Confirm arrangement?

Roomless guests will be moved around existing rooms and newly generated ones.
This will also confirm all rooms.

+
diff --git a/utils.py b/utils.py index cdd5e01..b2158cb 100644 --- a/utils.py +++ b/utils.py @@ -29,7 +29,6 @@ QUESTION_TYPES = { #https://docs.pretix.eu/en/latest/api/resources/questions.htm } TYPE_OF_QUESTIONS = {} # maps questionId -> type - async def load_questions() -> bool: global TYPE_OF_QUESTIONS # TYPE_OF_QUESTIONS.clear() It should not be needed