From 1b29784e6f3d90229e70879f904ef42cff884679 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 18:54:07 +0000 Subject: [PATCH 1/4] Initial plan From 5ce18566922ba60a99cb4e1d8eace215e4e75297 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 19:02:44 +0000 Subject: [PATCH 2/4] Implement orders pages with filters and actions Co-authored-by: ArcaEge <40526225+ArcaEge@users.noreply.github.com> --- drizzle/0025_greedy_krista_starr.sql | 2 + drizzle/meta/0025_snapshot.json | 1248 +++++++++++++++++ drizzle/meta/_journal.json | 7 + package-lock.json | 28 +- src/lib/server/db/schema.ts | 1 + .../admin/admin/orders/+page.server.ts | 172 ++- .../dashboard/admin/admin/orders/+page.svelte | 148 +- .../admin/admin/orders/[id]/+page.server.ts | 500 +++---- .../admin/admin/orders/[id]/+page.svelte | 321 +++-- .../market/item/[id]/+page.server.ts | 1 + 10 files changed, 1873 insertions(+), 555 deletions(-) create mode 100644 drizzle/0025_greedy_krista_starr.sql create mode 100644 drizzle/meta/0025_snapshot.json diff --git a/drizzle/0025_greedy_krista_starr.sql b/drizzle/0025_greedy_krista_starr.sql new file mode 100644 index 0000000..ca5e8f7 --- /dev/null +++ b/drizzle/0025_greedy_krista_starr.sql @@ -0,0 +1,2 @@ +ALTER TABLE "market_item_order" ADD COLUMN "marketItemId" integer NOT NULL;--> statement-breakpoint +ALTER TABLE "market_item_order" ADD CONSTRAINT "market_item_order_marketItemId_market_item_id_fk" FOREIGN KEY ("marketItemId") REFERENCES "public"."market_item"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0025_snapshot.json b/drizzle/meta/0025_snapshot.json new file mode 100644 index 0000000..222158d --- /dev/null +++ b/drizzle/meta/0025_snapshot.json @@ -0,0 +1,1248 @@ +{ + "id": "df45bdcb-ea12-47e9-8d07-e4348aa43961", + "prevId": "530bbae3-07b0-4ea9-bb7e-3d249c2632d1", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.impersonate_audit_log": { + "name": "impersonate_audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "adminUserId": { + "name": "adminUserId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetUserId": { + "name": "targetUserId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "impersonate_audit_log_adminUserId_user_id_fk": { + "name": "impersonate_audit_log_adminUserId_user_id_fk", + "tableFrom": "impersonate_audit_log", + "tableTo": "user", + "columnsFrom": [ + "adminUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "impersonate_audit_log_targetUserId_user_id_fk": { + "name": "impersonate_audit_log_targetUserId_user_id_fk", + "tableFrom": "impersonate_audit_log", + "tableTo": "user", + "columnsFrom": [ + "targetUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.legion_review": { + "name": "legion_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filamentUsed": { + "name": "filamentUsed", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "legion_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "legion_review_userId_user_id_fk": { + "name": "legion_review_userId_user_id_fk", + "tableFrom": "legion_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "legion_review_projectId_project_id_fk": { + "name": "legion_review_projectId_project_id_fk", + "tableFrom": "legion_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.market_item": { + "name": "market_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "createdBy": { + "name": "createdBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "minRequiredShopScore": { + "name": "minRequiredShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "minShopScore": { + "name": "minShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "maxShopScore": { + "name": "maxShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "maxPrice": { + "name": "maxPrice", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "minPrice": { + "name": "minPrice", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "isPublic": { + "name": "isPublic", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "market_item_createdBy_user_id_fk": { + "name": "market_item_createdBy_user_id_fk", + "tableFrom": "market_item", + "tableTo": "user", + "columnsFrom": [ + "createdBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.market_item_order": { + "name": "market_item_order", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "marketItemId": { + "name": "marketItemId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "addressId": { + "name": "addressId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bricksPaid": { + "name": "bricksPaid", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "market_order_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'awaiting_approval'" + }, + "userNotes": { + "name": "userNotes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "market_item_order_userId_user_id_fk": { + "name": "market_item_order_userId_user_id_fk", + "tableFrom": "market_item_order", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "market_item_order_marketItemId_market_item_id_fk": { + "name": "market_item_order_marketItemId_market_item_id_fk", + "tableFrom": "market_item_order", + "tableTo": "market_item", + "columnsFrom": [ + "marketItemId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ovenpheus_log": { + "name": "ovenpheus_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "bricksReceived": { + "name": "bricksReceived", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ovenpheus_log_userId_user_id_fk": { + "name": "ovenpheus_log_userId_user_id_fk", + "tableFrom": "ovenpheus_log", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "submittedToAirtable": { + "name": "submittedToAirtable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ship": { + "name": "ship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ship_userId_user_id_fk": { + "name": "ship_userId_user_id_fk", + "tableFrom": "ship", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ship_projectId_project_id_fk": { + "name": "ship_projectId_project_id_fk", + "tableFrom": "ship", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t2_review": { + "name": "t2_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shopScoreMultiplier": { + "name": "shopScoreMultiplier", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t2_review_userId_user_id_fk": { + "name": "t2_review_userId_user_id_fk", + "tableFrom": "t2_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t2_review_projectId_project_id_fk": { + "name": "t2_review_projectId_project_id_fk", + "tableFrom": "t2_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idvToken": { + "name": "idvToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.editor_file_type": { + "name": "editor_file_type", + "schema": "public", + "values": [ + "url", + "upload" + ] + }, + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.legion_action": { + "name": "legion_action", + "schema": "public", + "values": [ + "mark_for_printing", + "unmark_for_printing", + "print", + "add_comment", + "reject", + "already_printed" + ] + }, + "public.market_order_status": { + "name": "market_order_status", + "schema": "public", + "values": [ + "awaiting_approval", + "fulfilled", + "denied", + "refunded" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "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 f350c57..c234f99 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -176,6 +176,13 @@ "when": 1767390464394, "tag": "0024_bored_valeria_richards", "breakpoints": true + }, + { + "idx": 25, + "version": "7", + "when": 1768071572705, + "tag": "0025_greedy_krista_starr", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f6c020c..626ab37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -984,7 +984,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3028,7 +3027,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -3050,7 +3048,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.19.0 || >=20.6.0" }, @@ -3063,7 +3060,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -3079,7 +3075,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz", "integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.208.0", "import-in-the-middle": "^2.0.0", @@ -3467,7 +3462,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" @@ -3484,7 +3478,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", @@ -3502,7 +3495,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" } @@ -5309,7 +5301,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.47.0.tgz", "integrity": "sha512-mznN01MBXtr4T7X/E3ENkhF6GzqxTxL6/whG3OzCzUu8G8KYRNiCdoxLMVWAHJx/mDMPP3XAeKCMZHF/Xd/CDw==", "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", @@ -5348,7 +5339,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", @@ -5855,7 +5845,6 @@ "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.1", "@typescript-eslint/types": "8.46.1", @@ -6111,7 +6100,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6388,7 +6376,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -7002,7 +6989,6 @@ "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -7099,7 +7085,6 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -9399,7 +9384,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -9521,7 +9505,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -9694,7 +9677,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -9711,7 +9693,6 @@ "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" @@ -10017,7 +9998,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -10595,7 +10575,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.40.1.tgz", "integrity": "sha512-0R3t2oiLxJNJb2buz61MNfPdkjeyj2qTCM7TtIv/4ZfF12zD7Ig8iIo+C8febroy+9S4QJ7qfijtearSdO/1ww==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -10688,8 +10667,7 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", @@ -10854,7 +10832,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10913,7 +10890,6 @@ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.21.tgz", "integrity": "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==", "license": "MIT", - "peer": true, "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", @@ -11064,7 +11040,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz", "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -11640,7 +11615,6 @@ "integrity": "sha512-HwaJmXO3M1r4S8x2ea2vy8Rw/y/38HRQuK/gNDRQ7w9cJXn6xSl1sIIqKCffULSUjul3wV3I3Nd/GfbmsRReEA==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "bin": { "workerd": "bin/workerd" }, diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 2f4591e..977dfe9 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -249,6 +249,7 @@ export const marketOrderStatus = pgEnum('market_order_status', [ export const marketItemOrder = pgTable('market_item_order', { id: serial().primaryKey(), userId: integer().references(() => user.id).notNull(), + marketItemId: integer().references(() => marketItem.id).notNull(), addressId: text().notNull(), bricksPaid: integer().notNull(), diff --git a/src/routes/dashboard/admin/admin/orders/+page.server.ts b/src/routes/dashboard/admin/admin/orders/+page.server.ts index 64b2711..c89b8dc 100644 --- a/src/routes/dashboard/admin/admin/orders/+page.server.ts +++ b/src/routes/dashboard/admin/admin/orders/+page.server.ts @@ -1,26 +1,28 @@ import { db } from '$lib/server/db/index.js'; -import { project, user, devlog } from '$lib/server/db/schema.js'; +import { marketItemOrder, marketItem, user } from '$lib/server/db/schema.js'; import { error } from '@sveltejs/kit'; -import { eq, and, sql, ne, inArray } from 'drizzle-orm'; +import { eq, and, ne, inArray, sql, desc } from 'drizzle-orm'; import type { Actions } from './$types'; +import { decrypt } from '$lib/server/encryption'; +import { getUserData } from '$lib/server/idvUserData'; export async function load({ locals }) { if (!locals.user) { throw error(500); } - if (!locals.user.hasT2Review) { + if (!locals.user.hasAdmin) { throw error(403, { message: 'oi get out' }); } - const projects = await getProjects(['printed'], [], []); + const orders = await getOrders([], [], [], []); - const allProjects = await db + const allMarketItems = await db .select({ - id: project.id, - name: project.name + id: marketItem.id, + name: marketItem.name }) - .from(project) - .where(and(eq(project.deleted, false))); + .from(marketItem) + .where(eq(marketItem.deleted, false)); const users = await db .select({ @@ -30,10 +32,44 @@ export async function load({ locals }) { .from(user) .where(and(ne(user.trust, 'red'), ne(user.hackatimeTrust, 'red'))); // hide banned users + // Get unique countries from orders + const ordersWithAddresses = await db + .select({ + userId: marketItemOrder.userId, + addressId: marketItemOrder.addressId + }) + .from(marketItemOrder) + .where(eq(marketItemOrder.deleted, false)); + + const countries: string[] = []; + for (const order of ordersWithAddresses) { + try { + const orderUser = await db + .select({ + idvToken: user.idvToken + }) + .from(user) + .where(eq(user.id, order.userId)) + .limit(1); + + if (orderUser[0]?.idvToken) { + const token = decrypt(orderUser[0].idvToken); + const userData = await getUserData(token); + const address = userData?.addresses?.find((a: { id: string }) => a.id === order.addressId); + if (address?.country && !countries.includes(address.country)) { + countries.push(address.country); + } + } + } catch { + // Skip if we can't fetch address + } + } + return { - allProjects, - projects, - users + allMarketItems, + orders, + users, + countries: countries.sort() }; } @@ -42,79 +78,103 @@ export const actions = { if (!locals.user) { throw error(500); } - if (!locals.user.hasT2Review) { + if (!locals.user.hasAdmin) { throw error(403, { message: 'oi get out' }); } const data = await request.formData(); - const statusFilter = data.getAll('status') as (typeof project.status._.data)[]; + const statusFilter = data.getAll('status') as (typeof marketItemOrder.status._.data)[]; - const projectFilter = data.getAll('project').map((projectId) => { - const parsedInt = parseInt(projectId.toString()); - if (!parsedInt) throw error(400, { message: 'malformed project filter' }); - return parseInt(projectId.toString()); + const marketItemFilter = data.getAll('marketItem').map((itemId) => { + const parsedInt = parseInt(itemId.toString()); + if (!parsedInt) throw error(400, { message: 'malformed market item filter' }); + return parsedInt; }); const userFilter = data.getAll('user').map((userId) => { const parsedInt = parseInt(userId.toString()); if (!parsedInt) throw error(400, { message: 'malformed user filter' }); - return parseInt(userId.toString()); + return parsedInt; }); - const projects = await getProjects(statusFilter, projectFilter, userFilter); + const countryFilter = data.getAll('country') as string[]; + + const orders = await getOrders(statusFilter, marketItemFilter, userFilter, countryFilter); return { - projects, + orders, fields: { status: statusFilter, - project: projectFilter, - user: userFilter + marketItem: marketItemFilter, + user: userFilter, + country: countryFilter } }; } } satisfies Actions; -async function getProjects( - statusFilter: (typeof project.status._.data)[], - projectFilter: number[], - userFilter: number[] +async function getOrders( + statusFilter: (typeof marketItemOrder.status._.data)[], + marketItemFilter: number[], + userFilter: number[], + countryFilter: string[] ) { - return await db + const baseOrders = await db .select({ - project: { - id: project.id, - name: project.name, - description: project.description, - url: project.url, - createdAt: project.createdAt, - status: project.status + order: { + id: marketItemOrder.id, + userId: marketItemOrder.userId, + marketItemId: marketItemOrder.marketItemId, + addressId: marketItemOrder.addressId, + bricksPaid: marketItemOrder.bricksPaid, + status: marketItemOrder.status, + userNotes: marketItemOrder.userNotes, + notes: marketItemOrder.notes, + createdAt: marketItemOrder.createdAt + }, + marketItem: { + id: marketItem.id, + name: marketItem.name, + image: marketItem.image }, user: { id: user.id, - name: user.name - }, - timeSpent: sql`COALESCE(SUM(${devlog.timeSpent}), 0)`, - devlogCount: sql`COALESCE(COUNT(${devlog.id}), 0)` + name: user.name, + idvToken: user.idvToken + } }) - .from(project) - .leftJoin(devlog, and(eq(project.id, devlog.projectId), eq(devlog.deleted, false))) - .leftJoin(user, eq(user.id, project.userId)) + .from(marketItemOrder) + .leftJoin(marketItem, eq(marketItem.id, marketItemOrder.marketItemId)) + .leftJoin(user, eq(user.id, marketItemOrder.userId)) .where( and( - eq(project.deleted, false), - statusFilter.length > 0 ? inArray(project.status, statusFilter) : undefined, - projectFilter.length > 0 ? inArray(project.id, projectFilter) : undefined, - userFilter.length > 0 ? inArray(project.userId, userFilter) : undefined + eq(marketItemOrder.deleted, false), + statusFilter.length > 0 ? inArray(marketItemOrder.status, statusFilter) : undefined, + marketItemFilter.length > 0 ? inArray(marketItemOrder.marketItemId, marketItemFilter) : undefined, + userFilter.length > 0 ? inArray(marketItemOrder.userId, userFilter) : undefined ) ) - .groupBy( - project.id, - project.name, - project.description, - project.url, - project.createdAt, - project.status, - user.id, - user.name - ); + .orderBy(desc(marketItemOrder.createdAt)); + + // Filter by country if needed + if (countryFilter.length > 0) { + const filteredOrders = []; + for (const orderData of baseOrders) { + try { + if (orderData.user?.idvToken) { + const token = decrypt(orderData.user.idvToken); + const userData = await getUserData(token); + const address = userData?.addresses?.find((a: { id: string }) => a.id === orderData.order.addressId); + if (address?.country && countryFilter.includes(address.country)) { + filteredOrders.push(orderData); + } + } + } catch { + // Skip if we can't fetch address + } + } + return filteredOrders; + } + + return baseOrders; } diff --git a/src/routes/dashboard/admin/admin/orders/+page.svelte b/src/routes/dashboard/admin/admin/orders/+page.svelte index bf4957c..15d57e4 100644 --- a/src/routes/dashboard/admin/admin/orders/+page.svelte +++ b/src/routes/dashboard/admin/admin/orders/+page.svelte @@ -1,20 +1,25 @@ - +

Market item orders

- coming soon - - - - ---> - - ---> - - + + +
-
-

Leaderboard

-
- Coming soon! - --> - - + {/if}
diff --git a/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts b/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts index 342afae..a7f688d 100644 --- a/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts @@ -1,13 +1,12 @@ import { db } from '$lib/server/db/index.js'; -import { project, user, devlog, t2Review } from '$lib/server/db/schema.js'; +import { marketItemOrder, marketItem, user } from '$lib/server/db/schema.js'; import { error, fail, redirect } from '@sveltejs/kit'; -import { eq, and, asc, sql, desc } from 'drizzle-orm'; +import { eq, and } from 'drizzle-orm'; import type { Actions } from './$types'; import { sendSlackDM } from '$lib/server/slack.js'; -import { airtableBase } from '$lib/server/airtable'; -import { env } from '$env/dynamic/private'; import { decrypt } from '$lib/server/encryption'; import { getUserData } from '$lib/server/idvUserData'; +import { env } from '$env/dynamic/private'; export async function load({ locals, params }) { if (!locals.user) { @@ -19,248 +18,255 @@ export async function load({ locals, params }) { const id: number = parseInt(params.id); - // const [queriedProject] = await db - // .select({ - // project: { - // id: project.id, - // name: project.name, - // description: project.description, - - // url: project.url, - // editorFileType: project.editorFileType, - // editorUrl: project.editorUrl, - // uploadedFileUrl: project.uploadedFileUrl, - // modelFile: project.modelFile, - - // submittedToAirtable: project.submittedToAirtable, - - // createdAt: project.createdAt, - // updatedAt: project.updatedAt, - // status: project.status - // }, - // user: { - // id: user.id, - // name: user.name, - // slackID: user.slackId, - // trust: user.trust, - // hackatimeTrust: user.hackatimeTrust - // }, - // timeSpent: sql`COALESCE(SUM(${devlog.timeSpent}), 0)`, - // devlogCount: sql`COALESCE(COUNT(${devlog.id}), 0)` - // }) - // .from(project) - // .leftJoin(devlog, and(eq(project.id, devlog.projectId), eq(devlog.deleted, false))) - // .leftJoin(user, eq(user.id, project.userId)) - // .where(and(eq(project.id, id), eq(project.deleted, false))) - // .groupBy( - // project.id, - // project.name, - // project.description, - // project.url, - // project.editorFileType, - // project.editorUrl, - // project.uploadedFileUrl, - // project.modelFile, - // project.submittedToAirtable, - // project.createdAt, - // project.status, - // user.id, - // user.name, - // user.slackId, - // user.trust, - // user.hackatimeTrust - // ) - // .limit(1); - - // if (!queriedProject) { - // throw error(404, { message: 'project not found' }); - // } - - // const devlogs = await db - // .select() - // .from(devlog) - // .where(and(eq(devlog.projectId, queriedProject.project.id), eq(devlog.deleted, false))) - // .orderBy(asc(devlog.createdAt)); - - // return { - // project: queriedProject, - // devlogs, - // reviews: await getReviewHistory(id) - // }; + const [orderData] = await db + .select({ + order: { + id: marketItemOrder.id, + userId: marketItemOrder.userId, + marketItemId: marketItemOrder.marketItemId, + addressId: marketItemOrder.addressId, + bricksPaid: marketItemOrder.bricksPaid, + status: marketItemOrder.status, + userNotes: marketItemOrder.userNotes, + notes: marketItemOrder.notes, + createdAt: marketItemOrder.createdAt + }, + marketItem: { + id: marketItem.id, + name: marketItem.name, + description: marketItem.description, + image: marketItem.image + }, + user: { + id: user.id, + name: user.name, + slackId: user.slackId, + idvToken: user.idvToken, + brick: user.brick + } + }) + .from(marketItemOrder) + .leftJoin(marketItem, eq(marketItem.id, marketItemOrder.marketItemId)) + .leftJoin(user, eq(user.id, marketItemOrder.userId)) + .where(and(eq(marketItemOrder.id, id), eq(marketItemOrder.deleted, false))) + .limit(1); + + if (!orderData) { + throw error(404, { message: 'order not found' }); + } + + let address = null; + let userDataError = false; + + if (orderData.user?.idvToken) { + try { + const token = decrypt(orderData.user.idvToken); + const userData = await getUserData(token); + address = userData?.addresses?.find((a: { id: string }) => a.id === orderData.order.addressId); + } catch { + userDataError = true; + } + } else { + userDataError = true; + } + + return { + orderData, + address, + userDataError, + s3PublicUrl: env.S3_PUBLIC_URL + }; } -// export const actions = { -// default: async ({ locals, request, params }) => { -// if (!locals.user) { -// throw error(500); -// } -// if (!locals.user.hasT2Review) { -// throw error(403, { message: 'oi get out' }); -// } - -// const id: number = parseInt(params.id); - -// const [queriedProject] = await db -// .select({ -// project: { -// id: project.id, -// name: project.name, -// description: project.description, - -// url: project.url, -// editorFileType: project.editorFileType, -// editorUrl: project.editorUrl, -// uploadedFileUrl: project.uploadedFileUrl, -// submittedToAirtable: project.submittedToAirtable -// }, -// user: { -// id: user.id, -// name: user.name, -// slackId: user.slackId, -// idvId: user.idvId, -// idvToken: user.idvToken, -// trust: user.trust, -// hackatimeTrust: user.hackatimeTrust -// }, -// timeSpent: sql`COALESCE(SUM(${devlog.timeSpent}), 0)`, -// devlogCount: sql`COALESCE(COUNT(${devlog.id}), 0)` -// }) -// .from(project) -// .leftJoin(devlog, and(eq(project.id, devlog.projectId), eq(devlog.deleted, false))) -// .leftJoin(user, eq(user.id, project.userId)) -// .where(and(eq(project.id, id), eq(project.deleted, false))) -// .groupBy( -// project.id, -// project.name, -// project.description, -// project.url, -// project.editorFileType, -// project.editorUrl, -// project.uploadedFileUrl, -// user.id, -// user.name, -// user.slackId, -// user.idvId, -// user.idvToken, -// user.trust, -// user.hackatimeTrust -// ) -// .limit(1); - -// if (!queriedProject) { -// return error(404, { message: 'project not found' }); -// } - -// const data = await request.formData(); -// const notes = data.get('notes')?.toString(); -// const feedback = data.get('feedback')?.toString(); - -// if (notes === null || feedback === null) { -// return error(400); -// } - -// const status: typeof project.status._.data | undefined = 'finalized'; -// const statusMessage = 'finalised! :woah-dino:'; - -// if (airtableBase && !queriedProject.project.submittedToAirtable) { -// const [latestDevlog] = await db -// .select({ -// image: devlog.image -// }) -// .from(devlog) -// .where(and(eq(devlog.projectId, id), eq(devlog.deleted, false))) -// .orderBy(desc(devlog.createdAt)) -// .limit(1); - -// if (!queriedProject.user?.idvToken) { -// return fail(400, { -// message: 'IDV token revoked/expired, ask them to reauthenticate' -// }); -// } - -// const token = decrypt(queriedProject.user.idvToken); -// let userData; - -// try { -// userData = await getUserData(token); -// } catch { -// return fail(400, { -// message: 'IDV token revoked/expired, ask them to reauthenticate' -// }); -// } -// const { first_name, last_name, primary_email, birthday, addresses } = userData; - -// const address = addresses.find((address: { primary: boolean; }) => address.primary); - -// const repoUrl = -// queriedProject.project.editorFileType === 'upload' -// ? `${env.S3_PUBLIC_URL}/${queriedProject.project.uploadedFileUrl}` -// : queriedProject.project.editorFileType === 'url' -// ? queriedProject.project.editorUrl -// : ''; - -// const justificationAppend = `Project has ${queriedProject.devlogCount} ${queriedProject.devlogCount == 1 ? 'journal' : 'journals'} over ${Math.floor( -// queriedProject.timeSpent / 60 -// )}h ${queriedProject.timeSpent % 60}min, each one with a 3d model file to show progress.\nAll journals can be found here: https://construct.hackclub.com/dashboard/projects/${queriedProject.project.id}`; - -// await airtableBase('YSWS Project Submission').create({ -// 'Repository URL': repoUrl ?? '', -// 'Demo URL': queriedProject.project.url ?? '', -// Description: queriedProject.project.description ?? '', - -// 'First Name': first_name ?? '', -// 'Last Name': last_name ?? '', -// 'Email': primary_email ?? '', -// 'Birthday': birthday ?? '', -// 'Address (Line 1)': address?.line_1 ?? '', -// 'City': address?.city ?? '', -// 'State / Province': address?.state ?? '', -// 'Country': address?.country ?? '', -// 'ZIP / Postal Code': address?.postal_code ?? '', - -// 'Hours estimate': queriedProject.timeSpent / 60, -// 'Optional - Override Hours Spent': queriedProject.timeSpent / 60, -// 'Optional - Override Hours Spent Justification': notes -// ? notes + '\n' + justificationAppend -// : justificationAppend, -// Screenshot: [ -// { -// url: env.S3_PUBLIC_URL + '/' + latestDevlog.image -// } -// // eslint-disable-next-line @typescript-eslint/no-explicit-any -// ] as any, -// 'Slack Username': queriedProject.user?.name, -// idv_rec: queriedProject.user?.idvId, -// 'Identity Verified': true, -// 'Temp hold': false -// }); -// } - -// await db.insert(t2Review).values({ -// projectId: id, -// userId: locals.user.id, -// currencyMultiplier: 1.0, // TODO: implement -// notes, -// feedback -// }); - -// await db -// .update(project) -// .set({ -// status, -// submittedToAirtable: true -// }) -// .where(eq(project.id, id)); - -// if (queriedProject.user) { -// const feedbackText = feedback ? `\n\nHere's what they said:\n${feedback}` : ''; - -// await sendSlackDM( -// queriedProject.user.slackId, -// `Your project has been ${statusMessage}${feedbackText}` -// ); -// } - -// return redirect(302, '/dashboard/admin/review'); -// } -// } satisfies Actions; +export const actions = { + markShipped: async ({ locals, params }) => { + if (!locals.user) { + throw error(500); + } + if (!locals.user.hasAdmin) { + throw error(403, { message: 'oi get out' }); + } + + const id: number = parseInt(params.id); + + const [orderData] = await db + .select({ + order: { + id: marketItemOrder.id, + notes: marketItemOrder.notes + }, + marketItem: { + name: marketItem.name + }, + user: { + id: user.id, + slackId: user.slackId + } + }) + .from(marketItemOrder) + .leftJoin(marketItem, eq(marketItem.id, marketItemOrder.marketItemId)) + .leftJoin(user, eq(user.id, marketItemOrder.userId)) + .where(and(eq(marketItemOrder.id, id), eq(marketItemOrder.deleted, false))) + .limit(1); + + if (!orderData) { + throw error(404, { message: 'order not found' }); + } + + await db + .update(marketItemOrder) + .set({ + status: 'fulfilled' + }) + .where(eq(marketItemOrder.id, id)); + + if (orderData.user?.slackId) { + const notesText = orderData.order.notes ? `\n\nNotes: ${orderData.order.notes}` : ''; + await sendSlackDM( + orderData.user.slackId, + `Your order for ${orderData.marketItem?.name || 'a market item'} has been shipped! :package:${notesText}` + ); + } + + return { success: true, message: 'Order marked as shipped' }; + }, + + refund: async ({ locals, params, request }) => { + if (!locals.user) { + throw error(500); + } + if (!locals.user.hasAdmin) { + throw error(403, { message: 'oi get out' }); + } + + const id: number = parseInt(params.id); + + const [orderData] = await db + .select({ + order: { + id: marketItemOrder.id, + userId: marketItemOrder.userId, + bricksPaid: marketItemOrder.bricksPaid + }, + marketItem: { + name: marketItem.name + }, + user: { + id: user.id, + slackId: user.slackId, + brick: user.brick + } + }) + .from(marketItemOrder) + .leftJoin(marketItem, eq(marketItem.id, marketItemOrder.marketItemId)) + .leftJoin(user, eq(user.id, marketItemOrder.userId)) + .where(and(eq(marketItemOrder.id, id), eq(marketItemOrder.deleted, false))) + .limit(1); + + if (!orderData) { + throw error(404, { message: 'order not found' }); + } + + // Refund the bricks + await db + .update(user) + .set({ + brick: orderData.user!.brick + orderData.order.bricksPaid + }) + .where(eq(user.id, orderData.order.userId)); + + // Mark order as refunded + await db + .update(marketItemOrder) + .set({ + status: 'refunded' + }) + .where(eq(marketItemOrder.id, id)); + + if (orderData.user?.slackId) { + await sendSlackDM( + orderData.user.slackId, + `Your order for ${orderData.marketItem?.name || 'a market item'} has been refunded. You received ${orderData.order.bricksPaid} bricks back.` + ); + } + + return { success: true, message: 'Order refunded' }; + }, + + delete: async ({ locals, params }) => { + if (!locals.user) { + throw error(500); + } + if (!locals.user.hasAdmin) { + throw error(403, { message: 'oi get out' }); + } + + const id: number = parseInt(params.id); + + const [orderData] = await db + .select({ + order: { + id: marketItemOrder.id + }, + marketItem: { + name: marketItem.name + }, + user: { + slackId: user.slackId + } + }) + .from(marketItemOrder) + .leftJoin(marketItem, eq(marketItem.id, marketItemOrder.marketItemId)) + .leftJoin(user, eq(user.id, marketItemOrder.userId)) + .where(and(eq(marketItemOrder.id, id), eq(marketItemOrder.deleted, false))) + .limit(1); + + if (!orderData) { + throw error(404, { message: 'order not found' }); + } + + // Mark order as denied (deleted without refund) + await db + .update(marketItemOrder) + .set({ + status: 'denied', + deleted: true + }) + .where(eq(marketItemOrder.id, id)); + + if (orderData.user?.slackId) { + await sendSlackDM( + orderData.user.slackId, + `Your order for ${orderData.marketItem?.name || 'a market item'} has been denied.` + ); + } + + return redirect(302, '/dashboard/admin/admin/orders'); + }, + + updateNotes: async ({ locals, params, request }) => { + if (!locals.user) { + throw error(500); + } + if (!locals.user.hasAdmin) { + throw error(403, { message: 'oi get out' }); + } + + const id: number = parseInt(params.id); + const data = await request.formData(); + const notes = data.get('notes')?.toString(); + + if (notes === undefined) { + throw error(400, { message: 'missing notes' }); + } + + await db + .update(marketItemOrder) + .set({ + notes + }) + .where(eq(marketItemOrder.id, id)); + + return { success: true, message: 'Notes updated' }; + } +} satisfies Actions; diff --git a/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte b/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte index b7e4388..3bfb120 100644 --- a/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte +++ b/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte @@ -1,159 +1,192 @@ - - - -
- +
+ +

Admin Notes

+
+
{ + formPending = true; + return async ({ update }) => { + await update({ reset: false }); + formPending = false; + }; + }} + > + + + {#if form?.message} +

{form.message}

+ {/if} + + +
+
+ +

Actions

+
+ {#if data.orderData.order.status === 'awaiting_approval'} +
{ + formPending = true; + return async ({ update }) => { + await update({ reset: false }); + formPending = false; + }; + }} + onsubmit={() => { + return confirm('Mark this order as shipped?'); + }} + > + +
+ +
{ + formPending = true; + return async ({ update }) => { + await update({ reset: false }); + formPending = false; + }; + }} + onsubmit={() => { + return confirm('Refund this order? This will return the bricks to the user.'); + }} + > + +
+ +
{ + return confirm('Delete this order without refunding? This cannot be undone.'); + }} + > + +
+ {:else} +

+ Order is {marketOrderStatuses[data.orderData.order.status].toLowerCase()}. No actions available. +

+ {/if} +
diff --git a/src/routes/dashboard/market/item/[id]/+page.server.ts b/src/routes/dashboard/market/item/[id]/+page.server.ts index cb3d970..f40dd7c 100644 --- a/src/routes/dashboard/market/item/[id]/+page.server.ts +++ b/src/routes/dashboard/market/item/[id]/+page.server.ts @@ -101,6 +101,7 @@ export const actions = { await db.insert(marketItemOrder).values({ userId: locals.user.id, + marketItemId: id, addressId, bricksPaid: itemWithPrice.computedPrice, userNotes: notes From ced4a4dc8b515c1f15fc6d53a091f5fc82ba195e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 19:10:18 +0000 Subject: [PATCH 3/4] Fix linting issues in orders pages Co-authored-by: ArcaEge <40526225+ArcaEge@users.noreply.github.com> --- .../dashboard/admin/admin/orders/+page.server.ts | 2 +- src/routes/dashboard/admin/admin/orders/+page.svelte | 10 +++++----- .../dashboard/admin/admin/orders/[id]/+page.server.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/routes/dashboard/admin/admin/orders/+page.server.ts b/src/routes/dashboard/admin/admin/orders/+page.server.ts index c89b8dc..57360ec 100644 --- a/src/routes/dashboard/admin/admin/orders/+page.server.ts +++ b/src/routes/dashboard/admin/admin/orders/+page.server.ts @@ -1,7 +1,7 @@ import { db } from '$lib/server/db/index.js'; import { marketItemOrder, marketItem, user } from '$lib/server/db/schema.js'; import { error } from '@sveltejs/kit'; -import { eq, and, ne, inArray, sql, desc } from 'drizzle-orm'; +import { eq, and, ne, inArray, desc } from 'drizzle-orm'; import type { Actions } from './$types'; import { decrypt } from '$lib/server/encryption'; import { getUserData } from '$lib/server/idvUserData'; diff --git a/src/routes/dashboard/admin/admin/orders/+page.svelte b/src/routes/dashboard/admin/admin/orders/+page.svelte index 15d57e4..a46d72e 100644 --- a/src/routes/dashboard/admin/admin/orders/+page.svelte +++ b/src/routes/dashboard/admin/admin/orders/+page.svelte @@ -57,7 +57,7 @@ value={form?.fields.status ?? ['awaiting_approval']} multiple > - {#each Object.entries(marketOrderStatuses) as [status, longStatus]} + {#each Object.entries(marketOrderStatuses) as [status, longStatus] (status)} {/each} @@ -79,7 +79,7 @@ value={form?.fields.marketItem ?? []} multiple > - {#each filteredMarketItems as item} + {#each filteredMarketItems as item (item.id)} {/each} @@ -102,7 +102,7 @@ value={form?.fields.user ?? []} multiple > - {#each filteredUsers as user} + {#each filteredUsers as user (user?.id)} {/each} @@ -118,7 +118,7 @@ value={form?.fields.country ?? []} multiple > - {#each data.countries as country} + {#each data.countries as country (country)} {/each} @@ -147,7 +147,7 @@ {:else}
- {#each orders as order} + {#each orders as order (order.order.id)}
diff --git a/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts b/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts index a7f688d..7193ef8 100644 --- a/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts @@ -1,6 +1,6 @@ import { db } from '$lib/server/db/index.js'; import { marketItemOrder, marketItem, user } from '$lib/server/db/schema.js'; -import { error, fail, redirect } from '@sveltejs/kit'; +import { error, redirect } from '@sveltejs/kit'; import { eq, and } from 'drizzle-orm'; import type { Actions } from './$types'; import { sendSlackDM } from '$lib/server/slack.js'; @@ -131,7 +131,7 @@ export const actions = { return { success: true, message: 'Order marked as shipped' }; }, - refund: async ({ locals, params, request }) => { + refund: async ({ locals, params }) => { if (!locals.user) { throw error(500); } From b3b93bf24060f0ab8001bcc70639fedcaea12c62 Mon Sep 17 00:00:00 2001 From: Arca Ege Cengiz Date: Sun, 11 Jan 2026 22:50:19 +0000 Subject: [PATCH 4/4] Market order admin page --- drizzle/0025_greedy_krista_starr.sql | 2 +- drizzle/0026_wealthy_shinko_yamashiro.sql | 1 + drizzle/meta/0025_snapshot.json | 2 +- drizzle/meta/0026_snapshot.json | 1248 +++++++++++++++++ drizzle/meta/_journal.json | 7 + src/lib/server/db/schema.ts | 2 +- .../admin/admin/orders/+page.server.ts | 78 +- .../dashboard/admin/admin/orders/+page.svelte | 46 +- .../admin/admin/orders/[id]/+page.server.ts | 17 +- .../admin/admin/orders/[id]/+page.svelte | 184 ++- .../admin/admin/users/[id]/+page.server.ts | 2 +- .../admin/ysws-review/[id]/+page.server.ts | 2 +- .../market/item/[id]/+page.server.ts | 4 +- .../dashboard/market/ovenpheus/+page.svelte | 4 +- 14 files changed, 1389 insertions(+), 210 deletions(-) create mode 100644 drizzle/0026_wealthy_shinko_yamashiro.sql create mode 100644 drizzle/meta/0026_snapshot.json diff --git a/drizzle/0025_greedy_krista_starr.sql b/drizzle/0025_greedy_krista_starr.sql index ca5e8f7..69083a8 100644 --- a/drizzle/0025_greedy_krista_starr.sql +++ b/drizzle/0025_greedy_krista_starr.sql @@ -1,2 +1,2 @@ -ALTER TABLE "market_item_order" ADD COLUMN "marketItemId" integer NOT NULL;--> statement-breakpoint +ALTER TABLE "market_item_order" ADD COLUMN "marketItemId" integer;--> statement-breakpoint ALTER TABLE "market_item_order" ADD CONSTRAINT "market_item_order_marketItemId_market_item_id_fk" FOREIGN KEY ("marketItemId") REFERENCES "public"."market_item"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0026_wealthy_shinko_yamashiro.sql b/drizzle/0026_wealthy_shinko_yamashiro.sql new file mode 100644 index 0000000..54ac3fd --- /dev/null +++ b/drizzle/0026_wealthy_shinko_yamashiro.sql @@ -0,0 +1 @@ +-- ALTER TABLE "market_item_order" ALTER COLUMN "marketItemId" DROP NOT NULL; \ No newline at end of file diff --git a/drizzle/meta/0025_snapshot.json b/drizzle/meta/0025_snapshot.json index 222158d..555af2e 100644 --- a/drizzle/meta/0025_snapshot.json +++ b/drizzle/meta/0025_snapshot.json @@ -398,7 +398,7 @@ "name": "marketItemId", "type": "integer", "primaryKey": false, - "notNull": true + "notNull": false }, "addressId": { "name": "addressId", diff --git a/drizzle/meta/0026_snapshot.json b/drizzle/meta/0026_snapshot.json new file mode 100644 index 0000000..24e7535 --- /dev/null +++ b/drizzle/meta/0026_snapshot.json @@ -0,0 +1,1248 @@ +{ + "id": "d7a8ece8-d669-4251-96f7-6b98213f328c", + "prevId": "df45bdcb-ea12-47e9-8d07-e4348aa43961", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.impersonate_audit_log": { + "name": "impersonate_audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "adminUserId": { + "name": "adminUserId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetUserId": { + "name": "targetUserId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "impersonate_audit_log_adminUserId_user_id_fk": { + "name": "impersonate_audit_log_adminUserId_user_id_fk", + "tableFrom": "impersonate_audit_log", + "tableTo": "user", + "columnsFrom": [ + "adminUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "impersonate_audit_log_targetUserId_user_id_fk": { + "name": "impersonate_audit_log_targetUserId_user_id_fk", + "tableFrom": "impersonate_audit_log", + "tableTo": "user", + "columnsFrom": [ + "targetUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.legion_review": { + "name": "legion_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filamentUsed": { + "name": "filamentUsed", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "legion_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "legion_review_userId_user_id_fk": { + "name": "legion_review_userId_user_id_fk", + "tableFrom": "legion_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "legion_review_projectId_project_id_fk": { + "name": "legion_review_projectId_project_id_fk", + "tableFrom": "legion_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.market_item": { + "name": "market_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "createdBy": { + "name": "createdBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "minRequiredShopScore": { + "name": "minRequiredShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "minShopScore": { + "name": "minShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "maxShopScore": { + "name": "maxShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "maxPrice": { + "name": "maxPrice", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "minPrice": { + "name": "minPrice", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "isPublic": { + "name": "isPublic", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "market_item_createdBy_user_id_fk": { + "name": "market_item_createdBy_user_id_fk", + "tableFrom": "market_item", + "tableTo": "user", + "columnsFrom": [ + "createdBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.market_item_order": { + "name": "market_item_order", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "marketItemId": { + "name": "marketItemId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "addressId": { + "name": "addressId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bricksPaid": { + "name": "bricksPaid", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "market_order_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'awaiting_approval'" + }, + "userNotes": { + "name": "userNotes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "market_item_order_userId_user_id_fk": { + "name": "market_item_order_userId_user_id_fk", + "tableFrom": "market_item_order", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "market_item_order_marketItemId_market_item_id_fk": { + "name": "market_item_order_marketItemId_market_item_id_fk", + "tableFrom": "market_item_order", + "tableTo": "market_item", + "columnsFrom": [ + "marketItemId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ovenpheus_log": { + "name": "ovenpheus_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "bricksReceived": { + "name": "bricksReceived", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ovenpheus_log_userId_user_id_fk": { + "name": "ovenpheus_log_userId_user_id_fk", + "tableFrom": "ovenpheus_log", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "submittedToAirtable": { + "name": "submittedToAirtable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ship": { + "name": "ship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ship_userId_user_id_fk": { + "name": "ship_userId_user_id_fk", + "tableFrom": "ship", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ship_projectId_project_id_fk": { + "name": "ship_projectId_project_id_fk", + "tableFrom": "ship", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t2_review": { + "name": "t2_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shopScoreMultiplier": { + "name": "shopScoreMultiplier", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t2_review_userId_user_id_fk": { + "name": "t2_review_userId_user_id_fk", + "tableFrom": "t2_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t2_review_projectId_project_id_fk": { + "name": "t2_review_projectId_project_id_fk", + "tableFrom": "t2_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idvToken": { + "name": "idvToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.editor_file_type": { + "name": "editor_file_type", + "schema": "public", + "values": [ + "url", + "upload" + ] + }, + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.legion_action": { + "name": "legion_action", + "schema": "public", + "values": [ + "mark_for_printing", + "unmark_for_printing", + "print", + "add_comment", + "reject", + "already_printed" + ] + }, + "public.market_order_status": { + "name": "market_order_status", + "schema": "public", + "values": [ + "awaiting_approval", + "fulfilled", + "denied", + "refunded" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "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 c234f99..e960053 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -183,6 +183,13 @@ "when": 1768071572705, "tag": "0025_greedy_krista_starr", "breakpoints": true + }, + { + "idx": 26, + "version": "7", + "when": 1768171539001, + "tag": "0026_wealthy_shinko_yamashiro", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 977dfe9..8481c2b 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -249,7 +249,7 @@ export const marketOrderStatus = pgEnum('market_order_status', [ export const marketItemOrder = pgTable('market_item_order', { id: serial().primaryKey(), userId: integer().references(() => user.id).notNull(), - marketItemId: integer().references(() => marketItem.id).notNull(), + marketItemId: integer().references(() => marketItem.id), addressId: text().notNull(), bricksPaid: integer().notNull(), diff --git a/src/routes/dashboard/admin/admin/orders/+page.server.ts b/src/routes/dashboard/admin/admin/orders/+page.server.ts index 57360ec..22293f4 100644 --- a/src/routes/dashboard/admin/admin/orders/+page.server.ts +++ b/src/routes/dashboard/admin/admin/orders/+page.server.ts @@ -3,8 +3,6 @@ import { marketItemOrder, marketItem, user } from '$lib/server/db/schema.js'; import { error } from '@sveltejs/kit'; import { eq, and, ne, inArray, desc } from 'drizzle-orm'; import type { Actions } from './$types'; -import { decrypt } from '$lib/server/encryption'; -import { getUserData } from '$lib/server/idvUserData'; export async function load({ locals }) { if (!locals.user) { @@ -14,7 +12,7 @@ export async function load({ locals }) { throw error(403, { message: 'oi get out' }); } - const orders = await getOrders([], [], [], []); + const orders = await getOrders(['awaiting_approval'], [], []); const allMarketItems = await db .select({ @@ -32,44 +30,10 @@ export async function load({ locals }) { .from(user) .where(and(ne(user.trust, 'red'), ne(user.hackatimeTrust, 'red'))); // hide banned users - // Get unique countries from orders - const ordersWithAddresses = await db - .select({ - userId: marketItemOrder.userId, - addressId: marketItemOrder.addressId - }) - .from(marketItemOrder) - .where(eq(marketItemOrder.deleted, false)); - - const countries: string[] = []; - for (const order of ordersWithAddresses) { - try { - const orderUser = await db - .select({ - idvToken: user.idvToken - }) - .from(user) - .where(eq(user.id, order.userId)) - .limit(1); - - if (orderUser[0]?.idvToken) { - const token = decrypt(orderUser[0].idvToken); - const userData = await getUserData(token); - const address = userData?.addresses?.find((a: { id: string }) => a.id === order.addressId); - if (address?.country && !countries.includes(address.country)) { - countries.push(address.country); - } - } - } catch { - // Skip if we can't fetch address - } - } - return { allMarketItems, orders, - users, - countries: countries.sort() + users }; } @@ -97,17 +61,14 @@ export const actions = { return parsedInt; }); - const countryFilter = data.getAll('country') as string[]; - - const orders = await getOrders(statusFilter, marketItemFilter, userFilter, countryFilter); + const orders = await getOrders(statusFilter, marketItemFilter, userFilter); return { orders, fields: { status: statusFilter, marketItem: marketItemFilter, - user: userFilter, - country: countryFilter + user: userFilter } }; } @@ -116,10 +77,9 @@ export const actions = { async function getOrders( statusFilter: (typeof marketItemOrder.status._.data)[], marketItemFilter: number[], - userFilter: number[], - countryFilter: string[] + userFilter: number[] ) { - const baseOrders = await db + return await db .select({ order: { id: marketItemOrder.id, @@ -150,31 +110,11 @@ async function getOrders( and( eq(marketItemOrder.deleted, false), statusFilter.length > 0 ? inArray(marketItemOrder.status, statusFilter) : undefined, - marketItemFilter.length > 0 ? inArray(marketItemOrder.marketItemId, marketItemFilter) : undefined, + marketItemFilter.length > 0 + ? inArray(marketItemOrder.marketItemId, marketItemFilter) + : undefined, userFilter.length > 0 ? inArray(marketItemOrder.userId, userFilter) : undefined ) ) .orderBy(desc(marketItemOrder.createdAt)); - - // Filter by country if needed - if (countryFilter.length > 0) { - const filteredOrders = []; - for (const orderData of baseOrders) { - try { - if (orderData.user?.idvToken) { - const token = decrypt(orderData.user.idvToken); - const userData = await getUserData(token); - const address = userData?.addresses?.find((a: { id: string }) => a.id === orderData.order.addressId); - if (address?.country && countryFilter.includes(address.country)) { - filteredOrders.push(orderData); - } - } - } catch { - // Skip if we can't fetch address - } - } - return filteredOrders; - } - - return baseOrders; } diff --git a/src/routes/dashboard/admin/admin/orders/+page.svelte b/src/routes/dashboard/admin/admin/orders/+page.svelte index a46d72e..088623d 100644 --- a/src/routes/dashboard/admin/admin/orders/+page.svelte +++ b/src/routes/dashboard/admin/admin/orders/+page.svelte @@ -11,7 +11,7 @@ let orders = $derived(form?.orders ?? data.orders); const marketOrderStatuses = { - awaiting_approval: 'Awaiting Approval', + awaiting_approval: 'Pending', fulfilled: 'Fulfilled', denied: 'Denied', refunded: 'Refunded' @@ -31,7 +31,7 @@ -
+

Market item orders

@@ -47,7 +47,7 @@ }; }} > -
+
- + - - -
-

Orders ({orders.length})

+

+ Orders ({orders.length}) +

{#if orders.length == 0}
@@ -158,10 +145,10 @@ > {#if order.marketItem?.image} - {order.marketItem?.name {/if}

@@ -172,15 +159,12 @@ >{order.user?.name}

-

+

Bricks paid: {order.order.bricksPaid}

-
+

- Ordered + Ordered {relativeDate(order.order.createdAt)}

diff --git a/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts b/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts index 7193ef8..8fb97da 100644 --- a/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/admin/orders/[id]/+page.server.ts @@ -62,7 +62,9 @@ export async function load({ locals, params }) { try { const token = decrypt(orderData.user.idvToken); const userData = await getUserData(token); - address = userData?.addresses?.find((a: { id: string }) => a.id === orderData.order.addressId); + address = userData?.addresses?.find( + (a: { id: string }) => a.id === orderData.order.addressId + ); } catch { userDataError = true; } @@ -124,7 +126,7 @@ export const actions = { const notesText = orderData.order.notes ? `\n\nNotes: ${orderData.order.notes}` : ''; await sendSlackDM( orderData.user.slackId, - `Your order for ${orderData.marketItem?.name || 'a market item'} has been shipped! :package:${notesText}` + `Your order for ${orderData.marketItem?.name || 'a market item'} just got shipped! :package: :package: :package:\n\n${notesText}` ); } @@ -186,14 +188,14 @@ export const actions = { if (orderData.user?.slackId) { await sendSlackDM( orderData.user.slackId, - `Your order for ${orderData.marketItem?.name || 'a market item'} has been refunded. You received ${orderData.order.bricksPaid} bricks back.` + `Your order for ${orderData.marketItem?.name || 'a market item'} has been refunded! :oop:\nYou got your ${orderData.order.bricksPaid} bricks back` ); } return { success: true, message: 'Order refunded' }; }, - delete: async ({ locals, params }) => { + deny: async ({ locals, params }) => { if (!locals.user) { throw error(500); } @@ -225,19 +227,18 @@ export const actions = { throw error(404, { message: 'order not found' }); } - // Mark order as denied (deleted without refund) + // Mark order as denied await db .update(marketItemOrder) .set({ - status: 'denied', - deleted: true + status: 'denied' }) .where(eq(marketItemOrder.id, id)); if (orderData.user?.slackId) { await sendSlackDM( orderData.user.slackId, - `Your order for ${orderData.marketItem?.name || 'a market item'} has been denied.` + `Your order for ${orderData.marketItem?.name || 'a market item'} has been denied :dcolon:\nYou didn't get any of your bricks back :hmmm:` ); } diff --git a/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte b/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte index 3bfb120..f1dfe0a 100644 --- a/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte +++ b/src/routes/dashboard/admin/admin/orders/[id]/+page.svelte @@ -15,91 +15,98 @@ }; - + -
+

- Order #{data.orderData.order.id} - {data.orderData.marketItem?.name} + Order #{data.orderData.order.id}: {data.orderData.marketItem?.name}

-

Order Details

-
- {#if data.orderData.marketItem?.image} - {data.orderData.marketItem.name} - {/if} -
-

Market Item

-

{data.orderData.marketItem?.name}

-

{data.orderData.marketItem?.description}

-
+

Order details

-
-

User

-

- - {data.orderData.user?.name} - -

- {#if data.orderData.user?.slackId} +
+
+
+

{data.orderData.marketItem?.name}

+

{data.orderData.marketItem?.description}

+
+ + - -
-

Payment

-

Bricks paid: {data.orderData.order.bricksPaid}

-
+ {#if data.orderData.user?.slackId} +

+ Slack ID: {data.orderData.user.slackId} +

+ {/if} +
+ +
+

Payment

+

Bricks paid: {data.orderData.order.bricksPaid}

+
+ +
+

Status

+

{marketOrderStatuses[data.orderData.order.status]}

+
+ +
+

Shipping address

+ {#if data.userDataError} +

Failed to fetch user data, ask them to re-login

+ {:else if data.address} +

{data.address.line_1}

+ {#if data.address.line_2} +

{data.address.line_2}

+ {/if} +

+ {data.address.city}, {data.address.state}, + {data.address.postal_code} +

+

{data.address.country}

+ {:else} +

Address not found

+ {/if} +
-
-

Status

-

{marketOrderStatuses[data.orderData.order.status]}

-
+
+

User notes

+

{data.orderData.order.userNotes || 'None'}

+
-
-

Shipping Address

- {#if data.userDataError} -

Error fetching address data

- {:else if data.address} -

{data.address.line_1}

- {#if data.address.line_2} -

{data.address.line_2}

- {/if} +
+

Order date

- {data.address.city}, {data.address.state} {data.address.postal_code} + Created + {relativeDate(data.orderData.order.createdAt)} +

-

{data.address.country}

- {:else} -

Address not found

- {/if} -
- -
-

User Notes

-

{data.orderData.order.userNotes || 'None'}

+
-
-

Order Date

-

- Created - {relativeDate(data.orderData.order.createdAt)} - -

+ {#if data.orderData.marketItem?.image} +
+ {data.orderData.marketItem.name} +
+ {/if}
-

Admin Notes

-
+

Admin notes

+
{#if form?.message} -

{form.message}

+

{form.message}

{/if}

Actions

-
+
{#if data.orderData.order.status === 'awaiting_approval'}
@@ -163,29 +169,19 @@ formPending = false; }; }} - onsubmit={() => { - return confirm('Refund this order? This will return the bricks to the user.'); - }} > - -
{ - return confirm('Delete this order without refunding? This cannot be undone.'); - }} - > - + +
{:else}

- Order is {marketOrderStatuses[data.orderData.order.status].toLowerCase()}. No actions available. + Order is {marketOrderStatuses[data.orderData.order.status].toLowerCase()}, no actions + available

{/if}
diff --git a/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts b/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts index a83e6f3..8a8ba56 100644 --- a/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts @@ -289,10 +289,10 @@ export const actions = { }); } - const token = decrypt(queriedUser.idvToken); let userData; try { + const token = decrypt(queriedUser.idvToken); userData = await getUserData(token); } catch { return fail(400, { diff --git a/src/routes/dashboard/admin/ysws-review/[id]/+page.server.ts b/src/routes/dashboard/admin/ysws-review/[id]/+page.server.ts index 2c42de6..43a2768 100644 --- a/src/routes/dashboard/admin/ysws-review/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/ysws-review/[id]/+page.server.ts @@ -212,10 +212,10 @@ export const actions = { }); } - const token = decrypt(queriedProject.user.idvToken); let userData; try { + const token = decrypt(queriedProject.user.idvToken); userData = await getUserData(token); } catch { return fail(400, { diff --git a/src/routes/dashboard/market/item/[id]/+page.server.ts b/src/routes/dashboard/market/item/[id]/+page.server.ts index f40dd7c..4ef4cdc 100644 --- a/src/routes/dashboard/market/item/[id]/+page.server.ts +++ b/src/routes/dashboard/market/item/[id]/+page.server.ts @@ -20,10 +20,10 @@ export async function load({ locals, params }) { let addresses = null; if (locals.user.idvToken) { - const token = decrypt(locals.user.idvToken); let userData = null; try { + const token = decrypt(locals.user.idvToken); userData = await getUserData(token); } catch { userDataError = true; @@ -63,10 +63,10 @@ export const actions = { const itemWithPrice = await getItemWithPrice(id, locals); - const token = decrypt(locals.user.idvToken!); let userData = null; try { + const token = decrypt(locals.user.idvToken!); userData = await getUserData(token); } catch { throw error(403, { message: 'failed to fetch address, try logging out and back in' }); diff --git a/src/routes/dashboard/market/ovenpheus/+page.svelte b/src/routes/dashboard/market/ovenpheus/+page.svelte index 210ac31..df190c1 100644 --- a/src/routes/dashboard/market/ovenpheus/+page.svelte +++ b/src/routes/dashboard/market/ovenpheus/+page.svelte @@ -73,7 +73,9 @@ max={Math.floor(data.user.clay)} />
-

You'll get {bricks} bricks

+

+ You'll get {bricks} bricks and have {Math.floor(data.user.clay - clay)} left +