diff --git a/CONTEXT.md b/CONTEXT.md
index 4809491..bec8486 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -49,5 +49,9 @@ A capability label on a Machine (e.g. "oven", "grill", "assembly") describing wh
**Queue**:
The set of Preparation Steps awaiting a Machine carrying a given Machine Tag, ordered by turn. When such a Machine becomes idle, it takes the next eligible Step from that Tag's Queue.
+**Pickup Box**:
+A numbered locker at a Kitchen that a ready Order Item is placed in for the customer to collect. Each ready Order Item gets the lowest-numbered free box; if none is free, it waits for the next one to free up. The customer opens the box from their Order and closes it again after taking the item out — closing the box is what marks the Order Item picked up and frees the box.
+_Avoid_: Counter, shelf (the box is a lockable compartment the customer operates themselves)
+
**Operator**:
The human role attached to a Kitchen, responsible only for physically restocking ingredients and repairing machines. Never involved in cooking or fulfilling orders.
diff --git a/drizzle/0008_absurd_the_anarchist.sql b/drizzle/0008_absurd_the_anarchist.sql
new file mode 100644
index 0000000..4d9bfc3
--- /dev/null
+++ b/drizzle/0008_absurd_the_anarchist.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "pickup_boxes" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "number" integer NOT NULL,
+ "is_open" boolean DEFAULT false NOT NULL,
+ CONSTRAINT "pickup_boxes_number_unique" UNIQUE("number")
+);
+--> statement-breakpoint
+ALTER TABLE "order_items" ADD COLUMN "pickup_box_id" integer;--> statement-breakpoint
+ALTER TABLE "order_items" ADD CONSTRAINT "order_items_pickup_box_id_pickup_boxes_id_fk" FOREIGN KEY ("pickup_box_id") REFERENCES "public"."pickup_boxes"("id") ON DELETE no action ON UPDATE no action;
\ No newline at end of file
diff --git a/drizzle/meta/0008_snapshot.json b/drizzle/meta/0008_snapshot.json
new file mode 100644
index 0000000..2952012
--- /dev/null
+++ b/drizzle/meta/0008_snapshot.json
@@ -0,0 +1,942 @@
+{
+ "id": "42d1f3c1-18f8-42a3-ac41-9e29a4b14add",
+ "prevId": "1cec3157-9481-4f6a-83b0-a79064d2bed5",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.allergens": {
+ "name": "allergens",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "allergens_name_idx": {
+ "name": "allergens_name_idx",
+ "columns": [
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.brands": {
+ "name": "brands",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.contact_messages": {
+ "name": "contact_messages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.machine_tags": {
+ "name": "machine_tags",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "machine_tags_name_idx": {
+ "name": "machine_tags_name_idx",
+ "columns": [
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.machines": {
+ "name": "machines",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_tag_id": {
+ "name": "machine_tag_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "machines_machine_tag_id_machine_tags_id_fk": {
+ "name": "machines_machine_tag_id_machine_tags_id_fk",
+ "tableFrom": "machines",
+ "tableTo": "machine_tags",
+ "columnsFrom": [
+ "machine_tag_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.menu_item_allergens": {
+ "name": "menu_item_allergens",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "menu_item_id": {
+ "name": "menu_item_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allergen_id": {
+ "name": "allergen_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "menu_item_allergens_item_allergen_idx": {
+ "name": "menu_item_allergens_item_allergen_idx",
+ "columns": [
+ {
+ "expression": "menu_item_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "allergen_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "menu_item_allergens_menu_item_id_menu_items_id_fk": {
+ "name": "menu_item_allergens_menu_item_id_menu_items_id_fk",
+ "tableFrom": "menu_item_allergens",
+ "tableTo": "menu_items",
+ "columnsFrom": [
+ "menu_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "menu_item_allergens_allergen_id_allergens_id_fk": {
+ "name": "menu_item_allergens_allergen_id_allergens_id_fk",
+ "tableFrom": "menu_item_allergens",
+ "tableTo": "allergens",
+ "columnsFrom": [
+ "allergen_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.menu_item_options": {
+ "name": "menu_item_options",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "menu_item_id": {
+ "name": "menu_item_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price_delta_cents": {
+ "name": "price_delta_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "menu_item_options_menu_item_id_menu_items_id_fk": {
+ "name": "menu_item_options_menu_item_id_menu_items_id_fk",
+ "tableFrom": "menu_item_options",
+ "tableTo": "menu_items",
+ "columnsFrom": [
+ "menu_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.menu_item_steps": {
+ "name": "menu_item_steps",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "menu_item_id": {
+ "name": "menu_item_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sequence": {
+ "name": "sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_tag_id": {
+ "name": "machine_tag_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "estimated_duration_seconds": {
+ "name": "estimated_duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "menu_item_steps_menu_item_id_menu_items_id_fk": {
+ "name": "menu_item_steps_menu_item_id_menu_items_id_fk",
+ "tableFrom": "menu_item_steps",
+ "tableTo": "menu_items",
+ "columnsFrom": [
+ "menu_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "menu_item_steps_machine_tag_id_machine_tags_id_fk": {
+ "name": "menu_item_steps_machine_tag_id_machine_tags_id_fk",
+ "tableFrom": "menu_item_steps",
+ "tableTo": "machine_tags",
+ "columnsFrom": [
+ "machine_tag_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.menu_items": {
+ "name": "menu_items",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "brand_id": {
+ "name": "brand_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "price_cents": {
+ "name": "price_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "menu_items_brand_id_brands_id_fk": {
+ "name": "menu_items_brand_id_brands_id_fk",
+ "tableFrom": "menu_items",
+ "tableTo": "brands",
+ "columnsFrom": [
+ "brand_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.order_item_options": {
+ "name": "order_item_options",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "order_item_id": {
+ "name": "order_item_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price_delta_cents": {
+ "name": "price_delta_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "order_item_options_order_item_id_order_items_id_fk": {
+ "name": "order_item_options_order_item_id_order_items_id_fk",
+ "tableFrom": "order_item_options",
+ "tableTo": "order_items",
+ "columnsFrom": [
+ "order_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.order_item_steps": {
+ "name": "order_item_steps",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "order_item_id": {
+ "name": "order_item_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sequence": {
+ "name": "sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_tag_id": {
+ "name": "machine_tag_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "estimated_duration_seconds": {
+ "name": "estimated_duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "order_item_steps_order_item_id_order_items_id_fk": {
+ "name": "order_item_steps_order_item_id_order_items_id_fk",
+ "tableFrom": "order_item_steps",
+ "tableTo": "order_items",
+ "columnsFrom": [
+ "order_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "order_item_steps_machine_tag_id_machine_tags_id_fk": {
+ "name": "order_item_steps_machine_tag_id_machine_tags_id_fk",
+ "tableFrom": "order_item_steps",
+ "tableTo": "machine_tags",
+ "columnsFrom": [
+ "machine_tag_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.order_items": {
+ "name": "order_items",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "order_id": {
+ "name": "order_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "menu_item_id": {
+ "name": "menu_item_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price_cents": {
+ "name": "price_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'placed'"
+ },
+ "cancelled_by": {
+ "name": "cancelled_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pickup_box_id": {
+ "name": "pickup_box_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "order_items_order_id_orders_id_fk": {
+ "name": "order_items_order_id_orders_id_fk",
+ "tableFrom": "order_items",
+ "tableTo": "orders",
+ "columnsFrom": [
+ "order_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "order_items_menu_item_id_menu_items_id_fk": {
+ "name": "order_items_menu_item_id_menu_items_id_fk",
+ "tableFrom": "order_items",
+ "tableTo": "menu_items",
+ "columnsFrom": [
+ "menu_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "order_items_pickup_box_id_pickup_boxes_id_fk": {
+ "name": "order_items_pickup_box_id_pickup_boxes_id_fk",
+ "tableFrom": "order_items",
+ "tableTo": "pickup_boxes",
+ "columnsFrom": [
+ "pickup_box_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.orders": {
+ "name": "orders",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "customer_id": {
+ "name": "customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guest_name": {
+ "name": "guest_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guest_contact": {
+ "name": "guest_contact",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guest_access_token": {
+ "name": "guest_access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'placed'"
+ },
+ "payment_charge_id": {
+ "name": "payment_charge_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.pickup_boxes": {
+ "name": "pickup_boxes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "number": {
+ "name": "number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_open": {
+ "name": "is_open",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "pickup_boxes_number_unique": {
+ "name": "pickup_boxes_number_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "number"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.preparation_step_instances": {
+ "name": "preparation_step_instances",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "order_item_id": {
+ "name": "order_item_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sequence": {
+ "name": "sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "machine_tag_id": {
+ "name": "machine_tag_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "estimated_duration_seconds": {
+ "name": "estimated_duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "delay_seconds": {
+ "name": "delay_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "machine_id": {
+ "name": "machine_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "done_at": {
+ "name": "done_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "preparation_step_instances_order_item_id_order_items_id_fk": {
+ "name": "preparation_step_instances_order_item_id_order_items_id_fk",
+ "tableFrom": "preparation_step_instances",
+ "tableTo": "order_items",
+ "columnsFrom": [
+ "order_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "preparation_step_instances_machine_tag_id_machine_tags_id_fk": {
+ "name": "preparation_step_instances_machine_tag_id_machine_tags_id_fk",
+ "tableFrom": "preparation_step_instances",
+ "tableTo": "machine_tags",
+ "columnsFrom": [
+ "machine_tag_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "preparation_step_instances_machine_id_machines_id_fk": {
+ "name": "preparation_step_instances_machine_id_machines_id_fk",
+ "tableFrom": "preparation_step_instances",
+ "tableTo": "machines",
+ "columnsFrom": [
+ "machine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index 2bde7e8..5812ce8 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -57,6 +57,13 @@
"when": 1790804884144,
"tag": "0007_dazzling_norman_osborn",
"breakpoints": true
+ },
+ {
+ "idx": 8,
+ "version": "7",
+ "when": 1790845995013,
+ "tag": "0008_absurd_the_anarchist",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/src/components/orders/OrderStatusView.module.scss b/src/components/orders/OrderStatusView.module.scss
index f74a5ef..82f4ff4 100644
--- a/src/components/orders/OrderStatusView.module.scss
+++ b/src/components/orders/OrderStatusView.module.scss
@@ -299,6 +299,26 @@
}
}
+ .pickupButton {
+ overflow: hidden;
+ white-space: nowrap;
+ border-color: $color-text;
+ background: $color-text;
+ color: $color-background;
+ font-weight: 600;
+
+ &:hover:not(:disabled) {
+ border-color: $color-text;
+ opacity: 0.9;
+ }
+
+ @media (max-width: $breakpoint-tablet) {
+ flex-basis: 100%;
+ justify-content: center;
+ margin-bottom: $space-2;
+ }
+ }
+
.actionIcon {
width: 1rem;
height: 1rem;
diff --git a/src/components/orders/OrderStatusView.tsx b/src/components/orders/OrderStatusView.tsx
index 55a81f0..b236c91 100644
--- a/src/components/orders/OrderStatusView.tsx
+++ b/src/components/orders/OrderStatusView.tsx
@@ -8,8 +8,9 @@ import { useBasket } from '#/lib/basket.tsx'
import {
advanceSimulatedKitchen,
cancelOrderItem,
+ closePickupBox,
getOrderStatus,
- markOrderItemPickedUp,
+ openPickupBox,
} from '#/server/orders.ts'
import styles from './OrderStatusView.module.scss'
@@ -31,6 +32,9 @@ const getItemBanner = (item: OrderItemStatusView) => {
animationKey: 'done',
variant: 'ready',
label: 'Ready for pickup',
+ detail: item.pickupBox
+ ? `Pickup box ${item.pickupBox.number}`
+ : 'Waiting for a free pickup box',
} as const
}
if (item.status === 'picked_up') {
@@ -82,11 +86,6 @@ const BackArrowIcon = () => (
const actionIconPaths = {
buyAgain: ['M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8', 'M3 3v5h5'],
- pickedUp: [
- 'M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z',
- 'M3 6h18',
- 'M16 10a4 4 0 0 1-8 0',
- ],
help: [
'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Z',
'M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3',
@@ -112,6 +111,38 @@ const ActionIcon = ({ name }: { name: keyof typeof actionIconPaths }) => (
)
+const LOCK_DURATION_SECONDS = 0.25
+
+// The shackle lifts while the box is open, and drops back first when the
+// button leaves after closing, so the lock visibly snaps shut.
+const LockIcon = ({ open }: { open: boolean }) => {
+ const reduceMotion = useReducedMotion()
+ const transition = reduceMotion
+ ? { duration: 0 }
+ : { duration: LOCK_DURATION_SECONDS, ease: 'easeOut' as const }
+ return (
+
+ )
+}
+
const StepConnector = ({ filled }: { filled: boolean }) => {
const reduceMotion = useReducedMotion()
return (
@@ -194,6 +225,34 @@ export const OrderStatusView = ({
}
}
+ // Collapses to nothing on both axes, so the row closes the gap whether the
+ // button sits inline (desktop) or on its own full-width line (mobile); the
+ // negative margins cancel the flex gap it leaves behind.
+ const pickupButtonExit = reduceMotion
+ ? { opacity: 0, transition: { duration: 0 } }
+ : {
+ opacity: 0,
+ width: 0,
+ height: 0,
+ paddingTop: 0,
+ paddingBottom: 0,
+ paddingLeft: 0,
+ paddingRight: 0,
+ borderWidth: 0,
+ marginRight: -8,
+ marginBottom: -8,
+ transition: {
+ delay: LOCK_DURATION_SECONDS + 0.1,
+ duration: 0.3,
+ ease: 'easeInOut' as const,
+ },
+ }
+
+ const togglePickupBox = (item: OrderItemStatusView) => {
+ const toggle = item.pickupBox?.isOpen ? closePickupBox : openPickupBox
+ return toggle({ data: { orderItemId: item.id, guestAccessToken } })
+ }
+
const runKitchenSimulation = async () => {
setSimulating(true)
setError(null)
@@ -349,6 +408,25 @@ export const OrderStatusView = ({