diff --git a/opt/wildcat/__dev_openapi.json b/opt/wildcat/__dev_openapi.json index 51db21d..4ed63a0 100644 --- a/opt/wildcat/__dev_openapi.json +++ b/opt/wildcat/__dev_openapi.json @@ -9,21 +9,22 @@ "version": "0.1.0" }, "paths": { - "/v1/admin/credit/quote/{id}": { + "/v1/admin/credit/quote": { "get": { "tags": [ "crate::admin" ], - "operationId": "admin_lookup_quote", + "operationId": "list_quotes", "parameters": [ { - "name": "id", - "in": "path", - "description": "The quote id", - "required": true, + "name": "since", + "in": "query", + "description": "quotes younger than `since`", + "required": false, "schema": { "type": "string", - "format": "uuid" + "format": "date-time", + "nullable": true } } ], @@ -33,65 +34,63 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InfoReply" + "$ref": "#/components/schemas/ListReplyLight" } } } - }, - "404": { - "description": "Quote id not found" } } - }, - "post": { + } + }, + "/v1/admin/credit/quote/pending": { + "get": { "tags": [ "crate::admin" ], - "operationId": "resolve_quote", + "summary": "--------------------------- List quotes", + "operationId": "list_pending_quotes", "parameters": [ { - "name": "id", - "in": "path", - "description": "The quote id", - "required": true, + "name": "since", + "in": "query", + "description": "only quote requests younger than `since`", + "required": false, "schema": { - "type": "string" + "type": "string", + "format": "date-time", + "nullable": true } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResolveRequest" - } - } - }, - "required": true - }, "responses": { "200": { - "description": "Successful response" + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListReply" + } + } + } } } } }, - "/v1/admin/credit/quote/accepted": { + "/v1/admin/credit/quote/{id}": { "get": { "tags": [ "crate::admin" ], - "operationId": "list_accepted_quotes", + "operationId": "admin_lookup_quote", "parameters": [ { - "name": "since", - "in": "query", - "description": "only accepted quotes younger than `since`", - "required": false, + "name": "id", + "in": "path", + "description": "The quote id", + "required": true, "schema": { "type": "string", - "format": "date-time", - "nullable": true + "format": "uuid" } } ], @@ -101,41 +100,50 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListReply" + "$ref": "#/components/schemas/InfoReply" } } } + }, + "404": { + "description": "Quote id not found" } } - } - }, - "/v1/admin/credit/quote/pending": { - "get": { + }, + "post": { "tags": [ "crate::admin" ], - "summary": "--------------------------- List quotes", - "operationId": "list_pending_quotes", + "operationId": "admin_update_quote", "parameters": [ { - "name": "since", - "in": "query", - "description": "only quote requests younger than `since`", - "required": false, + "name": "id", + "in": "path", + "description": "The quote id", + "required": true, "schema": { "type": "string", - "format": "date-time", - "nullable": true + "format": "uuid" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateQuoteRequest" + } + } + }, + "required": true + }, "responses": { "200": { "description": "Successful response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListReply" + "$ref": "#/components/schemas/UpdateQuoteResponse" } } } @@ -165,7 +173,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Resolve" + "$ref": "#/components/schemas/ResolveOffer" } } }, @@ -269,9 +277,31 @@ "type": "string", "description": "Manually added - should be replaced with generated one." }, - "Resolve": { - "type": "string", - "description": "Manually added - should be replaced with generated one." + "ListReplyLight": { + "type": "object", + "required": [ + "quotes" + ], + "properties": { + "quotes": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string" + } + } + } + } + } }, "Amount": { "type": "integer", @@ -287,7 +317,7 @@ "drawee", "drawer", "payee", - "holder", + "endorsees", "sum", "maturity_date" ], @@ -298,8 +328,11 @@ "drawer": { "$ref": "#/components/schemas/IdentityPublicData" }, - "holder": { - "$ref": "#/components/schemas/IdentityPublicData" + "endorsees": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IdentityPublicData" + } }, "id": { "type": "string" @@ -628,7 +661,6 @@ }, "ListReply": { "type": "object", - "description": "--------------------------- List quotes", "required": [ "quotes" ], @@ -698,51 +730,6 @@ ], "description": "--------------------------- Resolve quote" }, - "ResolveRequest": { - "oneOf": [ - { - "type": "object", - "required": [ - "action" - ], - "properties": { - "action": { - "type": "string", - "enum": [ - "deny" - ] - } - } - }, - { - "type": "object", - "required": [ - "discount", - "action" - ], - "properties": { - "action": { - "type": "string", - "enum": [ - "offer" - ] - }, - "discount": { - "type": "string" - }, - "ttl": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - } - ], - "description": "--------------------------- Resolve quote request", - "discriminator": { - "propertyName": "action" - } - }, "StatusReply": { "oneOf": [ { @@ -754,7 +741,7 @@ "status": { "type": "string", "enum": [ - "pending" + "Pending" ] } } @@ -768,7 +755,7 @@ "status": { "type": "string", "enum": [ - "denied" + "Denied" ] } } @@ -794,7 +781,7 @@ "status": { "type": "string", "enum": [ - "offered" + "Offered" ] } } @@ -815,7 +802,7 @@ "status": { "type": "string", "enum": [ - "accepted" + "Accepted" ] } } @@ -830,7 +817,7 @@ "status": { "type": "string", "enum": [ - "rejected" + "Rejected" ] }, "tstamp": { @@ -845,6 +832,95 @@ "propertyName": "status" } }, + "UpdateQuoteRequest": { + "oneOf": [ + { + "type": "object", + "required": [ + "action" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "deny" + ] + } + } + }, + { + "type": "object", + "required": [ + "discount", + "action" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "offer" + ] + }, + "discount": { + "type": "string" + }, + "ttl": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + } + ], + "description": "--------------------------- Update quote status request", + "discriminator": { + "propertyName": "action" + } + }, + "UpdateQuoteResponse": { + "oneOf": [ + { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "denied" + ] + } + } + }, + { + "type": "object", + "required": [ + "discount", + "ttl", + "status" + ], + "properties": { + "discount": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "offered" + ] + }, + "ttl": { + "type": "string", + "format": "date-time" + } + } + } + ], + "discriminator": { + "propertyName": "status" + } + }, "Witness": { "oneOf": [ { diff --git a/opt/wildcat/openapi.json b/opt/wildcat/openapi.json index 151e407..3d9d65b 100644 --- a/opt/wildcat/openapi.json +++ b/opt/wildcat/openapi.json @@ -1 +1 @@ -{"openapi":"3.0.3","info":{"title":"bcr-wdc-quote-service","description":"","license":{"name":""},"version":"0.1.0"},"paths":{"/v1/admin/credit/quote/:id":{"post":{"tags":["crate::credit::admin"],"operationId":"resolve_quote","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveRequest"}}},"required":true},"responses":{"200":{"description":"Succesful response"}}}},"/v1/admin/credit/quote/accepted":{"get":{"tags":["crate::credit::admin"],"operationId":"list_accepted_quotes","parameters":[{"name":"since","in":"query","description":"only accepted quotes younger than `since`","required":false,"schema":{"type":"string","format":"date-time","nullable":true}}],"responses":{"200":{"description":"Succesful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListReply"}}}}}}},"/v1/admin/credit/quote/pending":{"get":{"tags":["crate::credit::admin"],"summary":"--------------------------- List quotes","operationId":"list_pending_quotes","parameters":[{"name":"since","in":"query","description":"only quote requests younger than `since`","required":false,"schema":{"type":"string","format":"date-time","nullable":true}}],"responses":{"200":{"description":"Succesful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListReply"}}}}}}},"/v1/admin/credit/quote/{id}":{"get":{"tags":["crate::credit::admin"],"operationId":"lookup_quote","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Succesful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InfoReply"}}}},"404":{"description":"Quote id not found"}}}},"/v1/credit/mint/quote":{"post":{"tags":["crate::credit::web"],"summary":"--------------------------- Enquire mint quote","operationId":"enquire_quote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnquireRequest"}}},"required":true},"responses":{"200":{"description":"Quote request admitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnquireReply"}}}},"404":{"description":"Quote request not accepted"}}}},"/v1/credit/mint/quote/{id}":{"get":{"tags":["crate::credit::web"],"operationId":"lookup_quote","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Succesful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusReply"}}}},"404":{"description":"Quote id not found"}}}},"/v1/credit/quote/{id}":{"post":{"tags":["crate::credit::web"],"operationId":"resolve_offer","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resolve"}}},"required":true},"responses":{"200":{"description":"Succesful response"},"404":{"description":"Quote not found"},"409":{"description":"Quote already resolved"}}}}},"components":{"schemas":{"Amount":{"type":"integer","format":"int64","description":"Amount can be any unit","minimum":0},"BillInfo":{"type":"object","description":"--------------------------- Enquire mint quote","required":["id","drawee","drawer","payee","holder","sum","maturity_date"],"properties":{"drawee":{"$ref":"#/components/schemas/IdentityPublicData"},"drawer":{"$ref":"#/components/schemas/IdentityPublicData"},"holder":{"$ref":"#/components/schemas/IdentityPublicData"},"id":{"type":"string"},"maturity_date":{"type":"string"},"payee":{"$ref":"#/components/schemas/IdentityPublicData"},"sum":{"type":"integer","format":"int64","minimum":0}}},"BlindSignature":{"type":"object","description":"Blind Signature (also called `promise`)","required":["amount","id","C_"],"properties":{"C_":{"type":"string","description":"Blinded signature (C_)\n\nThe blinded signature on the secret message `B_` of [BlindedMessage]."},"amount":{"$ref":"#/components/schemas/Amount"},"dleq":{"allOf":[{"$ref":"#/components/schemas/BlindSignatureDleq"}],"nullable":true},"id":{"$ref":"#/components/schemas/Id"}}},"BlindSignatureDleq":{"type":"object","description":"Blinded Signature on Dleq\n\nDefined in [NUT12](https://github.com/cashubtc/nuts/blob/main/12.md)","required":["e","s"],"properties":{"e":{"type":"string","description":"e"},"s":{"type":"string","description":"s"}}},"BlindedMessage":{"type":"object","description":"Blinded Message (also called `output`)","required":["amount","id","B_"],"properties":{"B_":{"type":"string","description":"Blinded secret message (B_)\n\nThe blinded secret message generated by the sender."},"amount":{"$ref":"#/components/schemas/Amount"},"id":{"$ref":"#/components/schemas/Id"},"witness":{"allOf":[{"$ref":"#/components/schemas/Witness"}],"nullable":true}}},"EnquireReply":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid"}}},"EnquireRequest":{"type":"object","description":"--------------------------- Enquire mint quote","required":["content","signature","outputs"],"properties":{"content":{"$ref":"#/components/schemas/BillInfo"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/BlindedMessage"}},"signature":{"type":"string"}}},"InfoReply":{"oneOf":[{"type":"object","required":["id","bill","submitted","suggested_expiration","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending"]},"submitted":{"type":"string","format":"date-time"},"suggested_expiration":{"type":"string","format":"date-time"}}},{"type":"object","required":["id","bill","ttl","signatures","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["offered"]},"ttl":{"type":"string","format":"date-time"}}},{"type":"object","required":["id","bill","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["denied"]}}},{"type":"object","required":["id","bill","signatures","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["accepted"]}}},{"type":"object","required":["id","bill","tstamp","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["rejected"]},"tstamp":{"type":"string","format":"date-time"}}}],"description":"--------------------------- Quote info request","discriminator":{"propertyName":"status"}},"ListReply":{"type":"object","description":"--------------------------- List quotes","required":["quotes"],"properties":{"quotes":{"type":"array","items":{"type":"string","format":"uuid"}}}},"ResolveOffer":{"oneOf":[{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["reject"]}}},{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["accept"]}}}],"description":"--------------------------- Resolve quote"},"ResolveRequest":{"oneOf":[{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["deny"]}}},{"type":"object","required":["discount","action"],"properties":{"action":{"type":"string","enum":["offer"]},"discount":{"type":"string"},"ttl":{"type":"string","format":"date-time","nullable":true}}}],"description":"--------------------------- Resolve quote request","discriminator":{"propertyName":"action"}},"StatusReply":{"oneOf":[{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["pending"]}}},{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["denied"]}}},{"type":"object","required":["signatures","expiration_date","status"],"properties":{"expiration_date":{"type":"string","format":"date-time"},"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["offered"]}}},{"type":"object","required":["signatures","status"],"properties":{"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["accepted"]}}},{"type":"object","required":["tstamp","status"],"properties":{"status":{"type":"string","enum":["rejected"]},"tstamp":{"type":"string","format":"date-time"}}}],"description":"--------------------------- Look up quote","discriminator":{"propertyName":"status"}},"Witness":{"oneOf":[{"$ref":"#/components/schemas/P2PKWitness"},{"$ref":"#/components/schemas/HTLCWitness"}],"description":"Witness"}}}} \ No newline at end of file +{"openapi":"3.0.3","info":{"title":"bcr-wdc-quote-service","description":"","license":{"name":""},"version":"0.1.0"},"paths":{"/v1/admin/credit/quote":{"get":{"tags":["crate::admin"],"operationId":"list_quotes","parameters":[{"name":"since","in":"query","description":"quotes younger than `since`","required":false,"schema":{"type":"string","format":"date-time","nullable":true}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.ListReplyLight"}}}}}}},"/v1/admin/credit/quote/pending":{"get":{"tags":["crate::admin"],"summary":"--------------------------- List quotes","operationId":"list_pending_quotes","parameters":[{"name":"since","in":"query","description":"only quote requests younger than `since`","required":false,"schema":{"type":"string","format":"date-time","nullable":true}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.ListReply"}}}}}}},"/v1/admin/credit/quote/{id}":{"get":{"tags":["crate::admin"],"operationId":"admin_lookup_quote","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.InfoReply"}}}},"404":{"description":"Quote id not found"}}},"post":{"tags":["crate::admin"],"operationId":"admin_update_quote","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.UpdateQuoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.UpdateQuoteResponse"}}}}}}},"/v1/credit/quote/{id}":{"post":{"tags":["crate::web"],"operationId":"resolve_offer","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.ResolveOffer"}}},"required":true},"responses":{"200":{"description":"Successful response"},"404":{"description":"Quote not found"},"409":{"description":"Quote already resolved"}}}},"/v1/mint/credit/quote":{"post":{"tags":["crate::web"],"summary":"--------------------------- Enquire mint quote","operationId":"enquire_quote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.EnquireRequest"}}},"required":true},"responses":{"200":{"description":"Quote request admitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.EnquireReply"}}}},"404":{"description":"Quote request not accepted"}}}},"/v1/mint/credit/quote/{id}":{"get":{"tags":["crate::web"],"operationId":"lookup_quote","parameters":[{"name":"id","in":"path","description":"The quote id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/web_quotes.StatusReply"}}}},"404":{"description":"Quote id not found"}}}}},"components":{"schemas":{"Amount":{"type":"integer","format":"int64","description":"Amount can be any unit","minimum":0},"BillInfo":{"type":"object","description":"--------------------------- Enquire mint quote","required":["id","drawee","drawer","payee","endorsees","sum","maturity_date"],"properties":{"drawee":{"$ref":"#/components/schemas/IdentityPublicData"},"drawer":{"$ref":"#/components/schemas/IdentityPublicData"},"endorsees":{"type":"array","items":{"$ref":"#/components/schemas/IdentityPublicData"}},"id":{"type":"string"},"maturity_date":{"type":"string"},"payee":{"$ref":"#/components/schemas/IdentityPublicData"},"sum":{"type":"integer","format":"int64","minimum":0}}},"BlindSignature":{"type":"object","description":"Blind Signature (also called `promise`)","required":["amount","id","C_"],"properties":{"C_":{"type":"string","description":"Blinded signature (C_)\n\nThe blinded signature on the secret message `B_` of [BlindedMessage]."},"amount":{"$ref":"#/components/schemas/Amount"},"dleq":{"allOf":[{"$ref":"#/components/schemas/BlindSignatureDleq"}],"nullable":true},"id":{"$ref":"#/components/schemas/Id"}}},"BlindSignatureDleq":{"type":"object","description":"Blinded Signature on Dleq\n\nDefined in [NUT12](https://github.com/cashubtc/nuts/blob/main/12.md)","required":["e","s"],"properties":{"e":{"type":"string","description":"e"},"s":{"type":"string","description":"s"}}},"BlindedMessage":{"type":"object","description":"Blinded Message (also called `output`)","required":["amount","id","B_"],"properties":{"B_":{"type":"string","description":"Blinded secret message (B_)\n\nThe blinded secret message generated by the sender."},"amount":{"$ref":"#/components/schemas/Amount"},"id":{"$ref":"#/components/schemas/Id"},"witness":{"allOf":[{"$ref":"#/components/schemas/Witness"}],"nullable":true}}},"ContactType":{"type":"string","enum":["Person","Company"]},"EnquireReply":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid"}}},"EnquireRequest":{"type":"object","description":"--------------------------- Enquire mint quote","required":["content","signature","outputs"],"properties":{"content":{"$ref":"#/components/schemas/BillInfo"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/BlindedMessage"}},"signature":{"type":"string"}}},"IdentityPublicData":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"},{"type":"object","required":["type","node_id","name"],"properties":{"email":{"type":"string","nullable":true},"name":{"type":"string"},"node_id":{"type":"string"},"nostr_relay":{"type":"string","nullable":true},"type":{"$ref":"#/components/schemas/ContactType"}}}]},"InfoReply":{"oneOf":[{"type":"object","required":["id","bill","submitted","suggested_expiration","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending"]},"submitted":{"type":"string","format":"date-time"},"suggested_expiration":{"type":"string","format":"date-time"}}},{"type":"object","required":["id","bill","ttl","signatures","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["offered"]},"ttl":{"type":"string","format":"date-time"}}},{"type":"object","required":["id","bill","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["denied"]}}},{"type":"object","required":["id","bill","signatures","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["accepted"]}}},{"type":"object","required":["id","bill","tstamp","status"],"properties":{"bill":{"$ref":"#/components/schemas/BillInfo"},"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["rejected"]},"tstamp":{"type":"string","format":"date-time"}}}],"description":"--------------------------- Quote info request","discriminator":{"propertyName":"status"}},"ListReply":{"type":"object","required":["quotes"],"properties":{"quotes":{"type":"array","items":{"type":"string","format":"uuid"}}}},"PostalAddress":{"type":"object","required":["country","city","address"],"properties":{"address":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"zip":{"type":"string","nullable":true}}},"ResolveOffer":{"oneOf":[{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["reject"]}}},{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["accept"]}}}],"description":"--------------------------- Resolve quote"},"StatusReply":{"oneOf":[{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["Pending"]}}},{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["Denied"]}}},{"type":"object","required":["signatures","expiration_date","status"],"properties":{"expiration_date":{"type":"string","format":"date-time"},"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["Offered"]}}},{"type":"object","required":["signatures","status"],"properties":{"signatures":{"type":"array","items":{"$ref":"#/components/schemas/BlindSignature"}},"status":{"type":"string","enum":["Accepted"]}}},{"type":"object","required":["tstamp","status"],"properties":{"status":{"type":"string","enum":["Rejected"]},"tstamp":{"type":"string","format":"date-time"}}}],"description":"--------------------------- Look up quote","discriminator":{"propertyName":"status"}},"UpdateQuoteRequest":{"oneOf":[{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["deny"]}}},{"type":"object","required":["discount","action"],"properties":{"action":{"type":"string","enum":["offer"]},"discount":{"type":"string"},"ttl":{"type":"string","format":"date-time","nullable":true}}}],"description":"--------------------------- Update quote status request","discriminator":{"propertyName":"action"}},"UpdateQuoteResponse":{"oneOf":[{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["denied"]}}},{"type":"object","required":["discount","ttl","status"],"properties":{"discount":{"type":"string"},"status":{"type":"string","enum":["offered"]},"ttl":{"type":"string","format":"date-time"}}}],"discriminator":{"propertyName":"status"}},"Witness":{"oneOf":[{"$ref":"#/components/schemas/P2PKWitness"},{"$ref":"#/components/schemas/HTLCWitness"}],"description":"Witness"}}}} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 529b14f..c81a2cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "lucide-react": "^0.479.0", "next-themes": "^0.4.6", "react": "^19.0.0", - "react-day-picker": "^9.6.2", + "react-day-picker": "^9.6.4", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", "react-router": "^7.3.0", @@ -7647,9 +7647,9 @@ } }, "node_modules/react-day-picker": { - "version": "9.6.2", - "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.6.2.tgz", - "integrity": "sha512-HVrTpDUYGKbi9scU9v2N3BPGMnL5jwekGFXcyyrvpamZdgYGfzVcovsBD4/yNGxhpCIX5X/5IoLDEAayNv1EqA==", + "version": "9.6.4", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.6.4.tgz", + "integrity": "sha512-OekyAWfaypSFN5zms4CD6Bcas5R0KbWdARkWTyQ2phJHQOolDfpLwrN6Q+U3ifPGNmKLf9ngXuSz25NKHMkR6w==", "license": "MIT", "dependencies": { "@date-fns/tz": "^1.2.0", diff --git a/package.json b/package.json index 78b5325..4a977b0 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "lucide-react": "^0.479.0", "next-themes": "^0.4.6", "react": "^19.0.0", - "react-day-picker": "^9.6.2", + "react-day-picker": "^9.6.4", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", "react-router": "^7.3.0", diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index a3c5fa6..b154ad1 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -34,7 +34,6 @@ const data = { { title: "Offered", url: "/quotes/offered", - disabled: true, }, { title: "Accepted", @@ -43,17 +42,14 @@ const data = { { title: "Denied", url: "/quotes/denied", - disabled: true, }, { title: "Rejected", url: "/quotes/rejected", - disabled: true, }, { title: "Expired", url: "/quotes/expired", - disabled: true, }, ], }, diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx index 77aa5aa..45af5f2 100644 --- a/src/components/ui/calendar.tsx +++ b/src/components/ui/calendar.tsx @@ -12,58 +12,62 @@ function Calendar({ ...props }: React.ComponentProps) { return ( - .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" : "[&:has([aria-selected])]:rounded-md" ), - day: cn( + day_button: cn( buttonVariants({ variant: "ghost" }), "size-8 p-0 font-normal aria-selected:opacity-100" ), - day_range_start: + range_start: "day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground", - day_range_end: + range_middle: + "aria-selected:bg-accent aria-selected:text-accent-foreground", + range_end: "day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground", - day_selected: + selected: "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", - day_today: "bg-accent text-accent-foreground", - day_outside: + today: "bg-accent text-accent-foreground", + outside: "day-outside text-muted-foreground aria-selected:text-muted-foreground", - day_disabled: "text-muted-foreground opacity-50", - day_range_middle: - "aria-selected:bg-accent aria-selected:text-accent-foreground", - day_hidden: "invisible", + disabled: "text-muted-foreground opacity-50", + hidden: "invisible", ...classNames, }} components={{ - PreviousMonthButton: ({ className }) => ( - - ), - NextMonthButton: ({ className }) => ( - - ), + Chevron: (props) => { + if (props.orientation === "left") { + return ; + } + return ; + }, }} {...props} /> diff --git a/src/constants/endpoints.ts b/src/constants/endpoints.ts index dcd218f..29b28c3 100644 --- a/src/constants/endpoints.ts +++ b/src/constants/endpoints.ts @@ -1,19 +1,11 @@ const INFO = "/v1/info" const BALANCES = "/v1/balances" +const ADMIN_QUOTE = "/v1/admin/credit/quote" const ADMIN_QUOTE_PENDING = "/v1/admin/credit/quote/pending" -const ADMIN_QUOTE_ACCEPTED = "/v1/admin/credit/quote/accepted" const ADMIN_QUOTE_BY_ID = "/v1/admin/credit/quote/:id" const CREDIT_QUOTE = "/v1/credit/mint/quote" const CREDIT_QUOTES_BY_ID = "/v1/credit/mint/quote/:id" -export { - INFO, - BALANCES, - ADMIN_QUOTE_PENDING, - ADMIN_QUOTE_ACCEPTED, - ADMIN_QUOTE_BY_ID, - CREDIT_QUOTE, - CREDIT_QUOTES_BY_ID, -} +export { INFO, BALANCES, ADMIN_QUOTE, ADMIN_QUOTE_PENDING, ADMIN_QUOTE_BY_ID, CREDIT_QUOTE, CREDIT_QUOTES_BY_ID } diff --git a/src/generated/client/@tanstack/react-query.gen.ts b/src/generated/client/@tanstack/react-query.gen.ts index f8220a7..34a9ead 100644 --- a/src/generated/client/@tanstack/react-query.gen.ts +++ b/src/generated/client/@tanstack/react-query.gen.ts @@ -1,8 +1,8 @@ // This file is auto-generated by @hey-api/openapi-ts -import { type Options, adminLookupQuote, resolveQuote, listAcceptedQuotes, listPendingQuotes, resolveOffer, enquireQuote, lookupQuote } from '../sdk.gen'; +import { type Options, listQuotes, listPendingQuotes, adminLookupQuote, adminUpdateQuote, resolveOffer, enquireQuote, lookupQuote } from '../sdk.gen'; import { queryOptions, type UseMutationOptions, type DefaultError } from '@tanstack/react-query'; -import type { AdminLookupQuoteData, ResolveQuoteData, ListAcceptedQuotesData, ListPendingQuotesData, ResolveOfferData, EnquireQuoteData, EnquireQuoteResponse, LookupQuoteData } from '../types.gen'; +import type { ListQuotesData, ListPendingQuotesData, AdminLookupQuoteData, AdminUpdateQuoteData, AdminUpdateQuoteResponse, ResolveOfferData, EnquireQuoteData, EnquireQuoteResponse, LookupQuoteData } from '../types.gen'; import { client as _heyApiClient } from '../client.gen'; export type QueryKey = [ @@ -36,12 +36,12 @@ const createQueryKey = (id: string, options?: TOptions ]; }; -export const adminLookupQuoteQueryKey = (options: Options) => createQueryKey('adminLookupQuote', options); +export const listQuotesQueryKey = (options?: Options) => createQueryKey('listQuotes', options); -export const adminLookupQuoteOptions = (options: Options) => { +export const listQuotesOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { - const { data } = await adminLookupQuote({ + const { data } = await listQuotes({ ...options, ...queryKey[0], signal, @@ -49,16 +49,16 @@ export const adminLookupQuoteOptions = (options: Options) }); return data; }, - queryKey: adminLookupQuoteQueryKey(options) + queryKey: listQuotesQueryKey(options) }); }; -export const resolveQuoteQueryKey = (options: Options) => createQueryKey('resolveQuote', options); +export const listPendingQuotesQueryKey = (options?: Options) => createQueryKey('listPendingQuotes', options); -export const resolveQuoteOptions = (options: Options) => { +export const listPendingQuotesOptions = (options?: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { - const { data } = await resolveQuote({ + const { data } = await listPendingQuotes({ ...options, ...queryKey[0], signal, @@ -66,30 +66,16 @@ export const resolveQuoteOptions = (options: Options) => { }); return data; }, - queryKey: resolveQuoteQueryKey(options) + queryKey: listPendingQuotesQueryKey(options) }); }; -export const resolveQuoteMutation = (options?: Partial>) => { - const mutationOptions: UseMutationOptions> = { - mutationFn: async (localOptions) => { - const { data } = await resolveQuote({ - ...options, - ...localOptions, - throwOnError: true - }); - return data; - } - }; - return mutationOptions; -}; - -export const listAcceptedQuotesQueryKey = (options?: Options) => createQueryKey('listAcceptedQuotes', options); +export const adminLookupQuoteQueryKey = (options: Options) => createQueryKey('adminLookupQuote', options); -export const listAcceptedQuotesOptions = (options?: Options) => { +export const adminLookupQuoteOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { - const { data } = await listAcceptedQuotes({ + const { data } = await adminLookupQuote({ ...options, ...queryKey[0], signal, @@ -97,16 +83,16 @@ export const listAcceptedQuotesOptions = (options?: Options) => createQueryKey('listPendingQuotes', options); +export const adminUpdateQuoteQueryKey = (options: Options) => createQueryKey('adminUpdateQuote', options); -export const listPendingQuotesOptions = (options?: Options) => { +export const adminUpdateQuoteOptions = (options: Options) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { - const { data } = await listPendingQuotes({ + const { data } = await adminUpdateQuote({ ...options, ...queryKey[0], signal, @@ -114,10 +100,24 @@ export const listPendingQuotesOptions = (options?: Options>) => { + const mutationOptions: UseMutationOptions> = { + mutationFn: async (localOptions) => { + const { data } = await adminUpdateQuote({ + ...options, + ...localOptions, + throwOnError: true + }); + return data; + } + }; + return mutationOptions; +}; + export const resolveOfferQueryKey = (options: Options) => createQueryKey('resolveOffer', options); export const resolveOfferOptions = (options: Options) => { diff --git a/src/generated/client/sdk.gen.ts b/src/generated/client/sdk.gen.ts index 7771954..ca05239 100644 --- a/src/generated/client/sdk.gen.ts +++ b/src/generated/client/sdk.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { AdminLookupQuoteData, AdminLookupQuoteResponse, ResolveQuoteData, ListAcceptedQuotesData, ListAcceptedQuotesResponse, ListPendingQuotesData, ListPendingQuotesResponse, ResolveOfferData, EnquireQuoteData, EnquireQuoteResponse, LookupQuoteData, LookupQuoteResponse } from './types.gen'; +import type { ListQuotesData, ListQuotesResponse, ListPendingQuotesData, ListPendingQuotesResponse, AdminLookupQuoteData, AdminLookupQuoteResponse, AdminUpdateQuoteData, AdminUpdateQuoteResponse, ResolveOfferData, EnquireQuoteData, EnquireQuoteResponse, LookupQuoteData, LookupQuoteResponse } from './types.gen'; import { client as _heyApiClient } from './client.gen'; export type Options = ClientOptions & { @@ -18,6 +18,23 @@ export type Options; }; +export const listQuotes = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + url: '/v1/admin/credit/quote', + ...options + }); +}; + +/** + * --------------------------- List quotes + */ +export const listPendingQuotes = (options?: Options) => { + return (options?.client ?? _heyApiClient).get({ + url: '/v1/admin/credit/quote/pending', + ...options + }); +}; + export const adminLookupQuote = (options: Options) => { return (options.client ?? _heyApiClient).get({ url: '/v1/admin/credit/quote/{id}', @@ -25,8 +42,8 @@ export const adminLookupQuote = (options: }); }; -export const resolveQuote = (options: Options) => { - return (options.client ?? _heyApiClient).post({ +export const adminUpdateQuote = (options: Options) => { + return (options.client ?? _heyApiClient).post({ url: '/v1/admin/credit/quote/{id}', ...options, headers: { @@ -36,23 +53,6 @@ export const resolveQuote = (options: Opti }); }; -export const listAcceptedQuotes = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/v1/admin/credit/quote/accepted', - ...options - }); -}; - -/** - * --------------------------- List quotes - */ -export const listPendingQuotes = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ - url: '/v1/admin/credit/quote/pending', - ...options - }); -}; - export const resolveOffer = (options: Options) => { return (options.client ?? _heyApiClient).post({ url: '/v1/credit/quote/{id}', diff --git a/src/generated/client/types.gen.ts b/src/generated/client/types.gen.ts index 2021c90..7ab2f14 100644 --- a/src/generated/client/types.gen.ts +++ b/src/generated/client/types.gen.ts @@ -15,10 +15,12 @@ export type P2PkWitness = string; */ export type HtlcWitness = string; -/** - * Manually added - should be replaced with generated one. - */ -export type Resolve = string; +export type ListReplyLight = { + quotes: Array<{ + id: string; + status?: string; + }>; +}; /** * Amount can be any unit @@ -31,7 +33,7 @@ export type Amount = number; export type BillInfo = { drawee: IdentityPublicData; drawer: IdentityPublicData; - holder: IdentityPublicData; + endorsees: Array; id: string; maturity_date: string; payee: IdentityPublicData; @@ -138,9 +140,6 @@ export type InfoReply = { tstamp: string; }; -/** - * --------------------------- List quotes - */ export type ListReply = { quotes: Array; }; @@ -161,71 +160,93 @@ export type ResolveOffer = { action: 'accept'; }; -/** - * --------------------------- Resolve quote request - */ -export type ResolveRequest = { - action: 'deny'; -} | { - action: 'offer'; - discount: string; - ttl?: string | null; -}; - /** * --------------------------- Look up quote */ export type StatusReply = { - status: 'pending'; + status: 'Pending'; } | { - status: 'denied'; + status: 'Denied'; } | { expiration_date: string; signatures: Array; - status: 'offered'; + status: 'Offered'; } | { signatures: Array; - status: 'accepted'; + status: 'Accepted'; } | { - status: 'rejected'; + status: 'Rejected'; tstamp: string; }; +/** + * --------------------------- Update quote status request + */ +export type UpdateQuoteRequest = { + action: 'deny'; +} | { + action: 'offer'; + discount: string; + ttl?: string | null; +}; + +export type UpdateQuoteResponse = { + status: 'denied'; +} | { + discount: string; + status: 'offered'; + ttl: string; +}; + /** * Witness */ export type Witness = P2PkWitness | HtlcWitness; -export type AdminLookupQuoteData = { +export type ListQuotesData = { body?: never; - path: { + path?: never; + query?: { /** - * The quote id + * quotes younger than `since` */ - id: string; + since?: string | null; }; - query?: never; - url: '/v1/admin/credit/quote/{id}'; + url: '/v1/admin/credit/quote'; }; -export type AdminLookupQuoteErrors = { +export type ListQuotesResponses = { /** - * Quote id not found + * Successful response */ - 404: unknown; + 200: ListReplyLight; }; -export type AdminLookupQuoteResponses = { +export type ListQuotesResponse = ListQuotesResponses[keyof ListQuotesResponses]; + +export type ListPendingQuotesData = { + body?: never; + path?: never; + query?: { + /** + * only quote requests younger than `since` + */ + since?: string | null; + }; + url: '/v1/admin/credit/quote/pending'; +}; + +export type ListPendingQuotesResponses = { /** * Successful response */ - 200: InfoReply; + 200: ListReply; }; -export type AdminLookupQuoteResponse = AdminLookupQuoteResponses[keyof AdminLookupQuoteResponses]; +export type ListPendingQuotesResponse = ListPendingQuotesResponses[keyof ListPendingQuotesResponses]; -export type ResolveQuoteData = { - body: ResolveRequest; +export type AdminLookupQuoteData = { + body?: never; path: { /** * The quote id @@ -236,57 +257,45 @@ export type ResolveQuoteData = { url: '/v1/admin/credit/quote/{id}'; }; -export type ResolveQuoteResponses = { +export type AdminLookupQuoteErrors = { /** - * Successful response + * Quote id not found */ - 200: unknown; -}; - -export type ListAcceptedQuotesData = { - body?: never; - path?: never; - query?: { - /** - * only accepted quotes younger than `since` - */ - since?: string | null; - }; - url: '/v1/admin/credit/quote/accepted'; + 404: unknown; }; -export type ListAcceptedQuotesResponses = { +export type AdminLookupQuoteResponses = { /** * Successful response */ - 200: ListReply; + 200: InfoReply; }; -export type ListAcceptedQuotesResponse = ListAcceptedQuotesResponses[keyof ListAcceptedQuotesResponses]; +export type AdminLookupQuoteResponse = AdminLookupQuoteResponses[keyof AdminLookupQuoteResponses]; -export type ListPendingQuotesData = { - body?: never; - path?: never; - query?: { +export type AdminUpdateQuoteData = { + body: UpdateQuoteRequest; + path: { /** - * only quote requests younger than `since` + * The quote id */ - since?: string | null; + id: string; }; - url: '/v1/admin/credit/quote/pending'; + query?: never; + url: '/v1/admin/credit/quote/{id}'; }; -export type ListPendingQuotesResponses = { +export type AdminUpdateQuoteResponses = { /** * Successful response */ - 200: ListReply; + 200: UpdateQuoteResponse; }; -export type ListPendingQuotesResponse = ListPendingQuotesResponses[keyof ListPendingQuotesResponses]; +export type AdminUpdateQuoteResponse = AdminUpdateQuoteResponses[keyof AdminUpdateQuoteResponses]; export type ResolveOfferData = { - body: Resolve; + body: ResolveOffer; path: { /** * The quote id diff --git a/src/main.tsx b/src/main.tsx index aa96d0c..0258d61 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -16,6 +16,10 @@ import AcceptedQuotesPage from "./pages/quotes/AcceptedQuotesPage" import { Toaster } from "./components/ui/sonner" import EarningsPage from "./pages/balances/EarningsPage" import CashFlowPage from "./pages/balances/CashFlowPage" +import OfferedQuotesPage from "./pages/quotes/OfferedQuotesPage" +import DeniedQuotesPage from "./pages/quotes/DeniedQuotesPage" +import ExpiredQuotesPage from "./pages/quotes/ExpiredQuotesPage" +import RejectedQuotesPage from "./pages/quotes/RejectedQuotesPage" const queryClient = new QueryClient() @@ -40,6 +44,10 @@ void prepare().then(() => { } /> } /> } /> + } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/src/mocks/db.ts b/src/mocks/db.ts index d6ff18e..d6f4df4 100644 --- a/src/mocks/db.ts +++ b/src/mocks/db.ts @@ -1,4 +1,4 @@ -import { factory, nullable, oneOf, primaryKey } from "@mswjs/data" +import { factory, manyOf, nullable, oneOf, primaryKey } from "@mswjs/data" import { faker } from "@faker-js/faker" // Seed `faker` to ensure reproducible random values of model properties. @@ -25,7 +25,7 @@ export const db = factory({ id: primaryKey(String), drawee: nullable(oneOf("identity_public_data")), drawer: nullable(oneOf("identity_public_data")), - holder: nullable(oneOf("identity_public_data")), + endorsees: nullable(manyOf("identity_public_data")), payee: nullable(oneOf("identity_public_data")), sum: Number, maturity_date: String, @@ -105,8 +105,8 @@ const BILLS = Array.from(Array(AMOUNT_OF_BILLS).keys()).map((_, index) => ).toUTCString(), drawee: ALICE, drawer: BOB, - payee: ALICE, - holder: CHARLIE, + payee: ALICE, // payee: CHARLIE + endorsees: [CHARLIE], }), ) @@ -152,7 +152,8 @@ ACCEPTED_BILLS.forEach((bill) => { where: { id: { equals: bill.id } }, data: { ...bill, - holder: MINT, + endorsees: [MINT], + payee: MINT, }, }) }) diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index 0cf4dae..13068fd 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -1,6 +1,6 @@ import { + fetchAdminQuote, fetchAdminLookupQuote, - fetchAdminQuoteAccepted, fetchAdminQuotePending, updateAdminQuote, } from "./handlers/admin_quotes" @@ -10,8 +10,8 @@ import { fetchInfo } from "./handlers/info" export const handlers = [ fetchInfo, fetchBalances, + fetchAdminQuote, fetchAdminQuotePending, - fetchAdminQuoteAccepted, fetchAdminLookupQuote, updateAdminQuote, ] diff --git a/src/mocks/handlers/admin_quotes.ts b/src/mocks/handlers/admin_quotes.ts index 11f0021..bf7cb19 100644 --- a/src/mocks/handlers/admin_quotes.ts +++ b/src/mocks/handlers/admin_quotes.ts @@ -1,12 +1,13 @@ import { http, delay, HttpResponse, StrictResponse } from "msw" import { API_URL } from "@/constants/api" -import { ADMIN_QUOTE_BY_ID, ADMIN_QUOTE_PENDING, ADMIN_QUOTE_ACCEPTED } from "@/constants/endpoints" +import { ADMIN_QUOTE_BY_ID, ADMIN_QUOTE_PENDING, ADMIN_QUOTE } from "@/constants/endpoints" import { AdminLookupQuoteResponse, InfoReply, - ListAcceptedQuotesResponse, ListPendingQuotesResponse, - ResolveRequest, + ListReplyLight, + UpdateQuoteRequest, + UpdateQuoteResponse, } from "@/generated/client" import { db } from "../db" @@ -23,15 +24,23 @@ export const fetchAdminQuotePending = http.get( - `${API_URL}${ADMIN_QUOTE_ACCEPTED}`, - async () => { +export const fetchAdminQuote = http.get( + `${API_URL}${ADMIN_QUOTE}`, + async ({ request }) => { + const url = new URL(request.url) await delay(1_000) + let data = db.quotes.getAll() - const data = db.quotes.getAll().filter((it) => it.status === "accepted") + const states = url.searchParams.getAll("status") + if (states.length !== 0) { + data = data.filter((it) => states.includes(it.status ?? "")) + } return HttpResponse.json({ - quotes: data.map((it) => it.id), + quotes: data.map((it) => ({ + id: it.id, + status: it.status ?? undefined, + })), }) }, ) @@ -52,7 +61,7 @@ export const fetchAdminLookupQuote = http.get( +export const updateAdminQuote = http.post( `${API_URL}${ADMIN_QUOTE_BY_ID}`, async ({ params, request }) => { const { id } = params @@ -81,6 +90,6 @@ export const updateAdminQuote = http.post( data: quote, }) - return HttpResponse.json(updated as InfoReply) + return HttpResponse.json(updated as UpdateQuoteResponse) }, ) diff --git a/src/pages/quotes/AcceptedQuotesPage.tsx b/src/pages/quotes/AcceptedQuotesPage.tsx index 25afa34..d0c0e68 100644 --- a/src/pages/quotes/AcceptedQuotesPage.tsx +++ b/src/pages/quotes/AcceptedQuotesPage.tsx @@ -2,7 +2,8 @@ import { Breadcrumbs } from "@/components/Breadcrumbs" import { PageTitle } from "@/components/PageTitle" import { Button } from "@/components/ui/button" import { Skeleton } from "@/components/ui/skeleton" -import { listAcceptedQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" +import { ListQuotesData } from "@/generated/client" +import { listQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" import useLocalStorage from "@/hooks/use-local-storage" import { cn } from "@/lib/utils" import { useSuspenseQuery } from "@tanstack/react-query" @@ -27,7 +28,11 @@ function QuoteListAccepted() { const navigate = useNavigate() const { data, isFetching } = useSuspenseQuery({ - ...listAcceptedQuotesOptions(), + ...listQuotesOptions({ + query: { + status: "accepted", + } as unknown as ListQuotesData["query"], + }), }) return ( @@ -48,10 +53,10 @@ function QuoteListAccepted() {
{isFetching ? ( - <>{it} + <>{it.id} ) : ( <> - {it} + {it.id} )} @@ -60,7 +65,7 @@ function QuoteListAccepted() { size="sm" disabled={isFetching} onClick={() => { - void navigate("/quotes/:id".replace(":id", it)) + void navigate("/quotes/:id".replace(":id", it.id)) }} > View @@ -77,7 +82,11 @@ function DevSection() { const [devMode] = useLocalStorage("devMode", false) const { data: quotesAccepted } = useSuspenseQuery({ - ...listAcceptedQuotesOptions({}), + ...listQuotesOptions({ + query: { + status: "accepted", + } as unknown as ListQuotesData["query"], + }), }) return ( diff --git a/src/pages/quotes/DeniedQuotesPage.tsx b/src/pages/quotes/DeniedQuotesPage.tsx new file mode 100644 index 0000000..6e0f5cd --- /dev/null +++ b/src/pages/quotes/DeniedQuotesPage.tsx @@ -0,0 +1,136 @@ +import { Breadcrumbs } from "@/components/Breadcrumbs" +import { PageTitle } from "@/components/PageTitle" +import { Button } from "@/components/ui/button" +import { Skeleton } from "@/components/ui/skeleton" +import { ListQuotesData } from "@/generated/client" +import { listQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" +import useLocalStorage from "@/hooks/use-local-storage" +import { cn } from "@/lib/utils" +import { useSuspenseQuery } from "@tanstack/react-query" +import { LoaderIcon } from "lucide-react" +import { Suspense } from "react" +import { Link, useNavigate } from "react-router" + +function Loader() { + return ( +
+ + + + + + +
+ ) +} + +function QuoteListDenied() { + const navigate = useNavigate() + + const { data, isFetching } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "denied", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> +
+ +
+ +
+ {data.quotes.length === 0 &&
No denied quotes.
} + {data.quotes.map((it, index) => { + return ( +
+ + {isFetching ? ( + <>{it.id} + ) : ( + <> + {it.id} + + )} + + + +
+ ) + })} +
+ + ) +} + +function DevSection() { + const [devMode] = useLocalStorage("devMode", false) + + const { data: quotesDenied } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "denied", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> + {devMode && ( + <> +
+            {JSON.stringify(quotesDenied, null, 2)}
+          
+ + )} + + ) +} + +function PageBody() { + return ( +
+
+ +
+
+ ) +} + +export default function DeniedQuotesPage() { + return ( + <> + + Quotes + , + ]} + > + Denied + + Denied Quotes + }> + + + + + + + ) +} diff --git a/src/pages/quotes/ExpiredQuotesPage.tsx b/src/pages/quotes/ExpiredQuotesPage.tsx new file mode 100644 index 0000000..9464a03 --- /dev/null +++ b/src/pages/quotes/ExpiredQuotesPage.tsx @@ -0,0 +1,136 @@ +import { Breadcrumbs } from "@/components/Breadcrumbs" +import { PageTitle } from "@/components/PageTitle" +import { Button } from "@/components/ui/button" +import { Skeleton } from "@/components/ui/skeleton" +import { ListQuotesData } from "@/generated/client" +import { listQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" +import useLocalStorage from "@/hooks/use-local-storage" +import { cn } from "@/lib/utils" +import { useSuspenseQuery } from "@tanstack/react-query" +import { LoaderIcon } from "lucide-react" +import { Suspense } from "react" +import { Link, useNavigate } from "react-router" + +function Loader() { + return ( +
+ + + + + + +
+ ) +} + +function QuoteListExpired() { + const navigate = useNavigate() + + const { data, isFetching } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "expired", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> +
+ +
+ +
+ {data.quotes.length === 0 &&
No expired quotes.
} + {data.quotes.map((it, index) => { + return ( +
+ + {isFetching ? ( + <>{it.id} + ) : ( + <> + {it.id} + + )} + + + +
+ ) + })} +
+ + ) +} + +function DevSection() { + const [devMode] = useLocalStorage("devMode", false) + + const { data: quotesExpired } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "expired", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> + {devMode && ( + <> +
+            {JSON.stringify(quotesExpired, null, 2)}
+          
+ + )} + + ) +} + +function PageBody() { + return ( +
+
+ +
+
+ ) +} + +export default function ExpiredQuotesPage() { + return ( + <> + + Quotes + , + ]} + > + Expired + + Expired Quotes + }> + + + + + + + ) +} diff --git a/src/pages/quotes/OfferedQuotesPage.tsx b/src/pages/quotes/OfferedQuotesPage.tsx new file mode 100644 index 0000000..7cd2a98 --- /dev/null +++ b/src/pages/quotes/OfferedQuotesPage.tsx @@ -0,0 +1,136 @@ +import { Breadcrumbs } from "@/components/Breadcrumbs" +import { PageTitle } from "@/components/PageTitle" +import { Button } from "@/components/ui/button" +import { Skeleton } from "@/components/ui/skeleton" +import { ListQuotesData } from "@/generated/client" +import { listQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" +import useLocalStorage from "@/hooks/use-local-storage" +import { cn } from "@/lib/utils" +import { useSuspenseQuery } from "@tanstack/react-query" +import { LoaderIcon } from "lucide-react" +import { Suspense } from "react" +import { Link, useNavigate } from "react-router" + +function Loader() { + return ( +
+ + + + + + +
+ ) +} + +function QuoteListOffered() { + const navigate = useNavigate() + + const { data, isFetching } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "offered", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> +
+ +
+ +
+ {data.quotes.length === 0 &&
No offered quotes.
} + {data.quotes.map((it, index) => { + return ( +
+ + {isFetching ? ( + <>{it.id} + ) : ( + <> + {it.id} + + )} + + + +
+ ) + })} +
+ + ) +} + +function DevSection() { + const [devMode] = useLocalStorage("devMode", false) + + const { data: quotesOffered } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "offered", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> + {devMode && ( + <> +
+            {JSON.stringify(quotesOffered, null, 2)}
+          
+ + )} + + ) +} + +function PageBody() { + return ( +
+
+ +
+
+ ) +} + +export default function OfferedQuotesPage() { + return ( + <> + + Quotes + , + ]} + > + Offered + + Offered Quotes + }> + + + + + + + ) +} diff --git a/src/pages/quotes/PendingQuotesPage.tsx b/src/pages/quotes/PendingQuotesPage.tsx index 2dd6451..3f30c3e 100644 --- a/src/pages/quotes/PendingQuotesPage.tsx +++ b/src/pages/quotes/PendingQuotesPage.tsx @@ -82,7 +82,6 @@ function QuoteItemCard({ id, isLoading }: { id: InfoReply["id"]; isLoading: bool drawee={data.bill?.drawee} drawer={data.bill?.drawer} payee={data.bill?.payee} - holder={data.bill?.holder} className="gap-1.5" />
diff --git a/src/pages/quotes/QuotePage.tsx b/src/pages/quotes/QuotePage.tsx index c48717f..a1e117f 100644 --- a/src/pages/quotes/QuotePage.tsx +++ b/src/pages/quotes/QuotePage.tsx @@ -10,7 +10,7 @@ import { IdentityPublicData, InfoReply } from "@/generated/client" import { adminLookupQuoteOptions, adminLookupQuoteQueryKey, - resolveQuoteMutation, + adminUpdateQuoteMutation, } from "@/generated/client/@tanstack/react-query.gen" import useLocalStorage from "@/hooks/use-local-storage" import { cn } from "@/lib/utils" @@ -93,7 +93,7 @@ const TimeToLiveForm = ({ onSubmit, submitButtonText = "Submit" }: TimeToLiveFor mode="single" selected={ttl} onSelect={(day) => setValue("ttl", day)} - fromDate={addDays(new Date(Date.now()), 1)} + hidden={{ before: addDays(new Date(Date.now()), 1) }} /> @@ -224,7 +224,7 @@ function QuoteActions({ value, isFetching }: { value: InfoReply; isFetching: boo const queryClient = useQueryClient() const denyQuote = useMutation({ - ...resolveQuoteMutation(), + ...adminUpdateQuoteMutation(), onSettled: () => { toast.dismiss(`quote-${value.id}-deny`) }, @@ -244,7 +244,7 @@ function QuoteActions({ value, isFetching }: { value: InfoReply; isFetching: boo }, }) const offerQuote = useMutation({ - ...resolveQuoteMutation(), + ...adminUpdateQuoteMutation(), onSettled: () => { toast.dismiss(`quote-${value.id}-offer`) }, @@ -363,7 +363,6 @@ function QuoteActions({ value, isFetching }: { value: InfoReply; isFetching: boo export function ParticipantsOverviewCard({ drawee, drawer, - holder, payee, className, }: { @@ -385,7 +384,7 @@ export function ParticipantsOverviewCard({
- +
) @@ -477,7 +476,7 @@ function Quote({ value, isFetching }: { value: InfoReply; isFetching: boolean }) drawee={value.bill?.drawee} drawer={value.bill?.drawer} payee={value.bill?.payee} - holder={value.bill?.holder} + holder={value.bill?.payee} /> @@ -499,12 +498,6 @@ function Quote({ value, isFetching }: { value: InfoReply; isFetching: boolean }) - - Holder: - - - - diff --git a/src/pages/quotes/QuotesPage.tsx b/src/pages/quotes/QuotesPage.tsx index e16e417..d18874c 100644 --- a/src/pages/quotes/QuotesPage.tsx +++ b/src/pages/quotes/QuotesPage.tsx @@ -2,18 +2,23 @@ import { Suspense } from "react" import { Breadcrumbs } from "@/components/Breadcrumbs" import { PageTitle } from "@/components/PageTitle" import { Skeleton } from "@/components/ui/skeleton" -import { listAcceptedQuotesOptions, listPendingQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" +import { listQuotesOptions, listPendingQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" import { useSuspenseQuery } from "@tanstack/react-query" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { cn } from "@/lib/utils" import { Link } from "react-router" import { ChevronRight } from "lucide-react" +import { ListQuotesData } from "@/generated/client" function Loader() { return (
+ + + +
) } @@ -24,7 +29,43 @@ function PageBody() { }) const { data: quotesAccepted } = useSuspenseQuery({ - ...listAcceptedQuotesOptions({}), + ...listQuotesOptions({ + query: { + status: "accepted", + } as unknown as ListQuotesData["query"], + }), + }) + + const { data: quotesOffered } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "offered", + } as unknown as ListQuotesData["query"], + }), + }) + + const { data: quotesDenied } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "denied", + } as unknown as ListQuotesData["query"], + }), + }) + + const { data: quotesExpired } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "expired", + } as unknown as ListQuotesData["query"], + }), + }) + + const { data: quotesRejected } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "rejected", + } as unknown as ListQuotesData["query"], + }), }) return ( @@ -55,6 +96,21 @@ function PageBody() { + + +
+
+ + Offered quotes + + {quotesOffered.quotes.length} offered +
+
+ +
+
+
+
@@ -70,6 +126,51 @@ function PageBody() {
+ + +
+
+ + Denied quotes + + {quotesDenied.quotes.length} denied +
+
+ +
+
+
+ + + +
+
+ + Rejected quotes + + {quotesRejected.quotes.length} rejected +
+
+ +
+
+
+ + + +
+
+ + Expired quotes + + {quotesExpired.quotes.length} expired +
+
+ +
+
+
+ ) } diff --git a/src/pages/quotes/RejectedQuotesPage.tsx b/src/pages/quotes/RejectedQuotesPage.tsx new file mode 100644 index 0000000..7b2b68c --- /dev/null +++ b/src/pages/quotes/RejectedQuotesPage.tsx @@ -0,0 +1,136 @@ +import { Breadcrumbs } from "@/components/Breadcrumbs" +import { PageTitle } from "@/components/PageTitle" +import { Button } from "@/components/ui/button" +import { Skeleton } from "@/components/ui/skeleton" +import { ListQuotesData } from "@/generated/client" +import { listQuotesOptions } from "@/generated/client/@tanstack/react-query.gen" +import useLocalStorage from "@/hooks/use-local-storage" +import { cn } from "@/lib/utils" +import { useSuspenseQuery } from "@tanstack/react-query" +import { LoaderIcon } from "lucide-react" +import { Suspense } from "react" +import { Link, useNavigate } from "react-router" + +function Loader() { + return ( +
+ + + + + + +
+ ) +} + +function QuoteListRejected() { + const navigate = useNavigate() + + const { data, isFetching } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "rejected", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> +
+ +
+ +
+ {data.quotes.length === 0 &&
No rejected quotes.
} + {data.quotes.map((it, index) => { + return ( +
+ + {isFetching ? ( + <>{it.id} + ) : ( + <> + {it.id} + + )} + + + +
+ ) + })} +
+ + ) +} + +function DevSection() { + const [devMode] = useLocalStorage("devMode", false) + + const { data: quotesRejected } = useSuspenseQuery({ + ...listQuotesOptions({ + query: { + status: "rejected", + } as unknown as ListQuotesData["query"], + }), + }) + + return ( + <> + {devMode && ( + <> +
+            {JSON.stringify(quotesRejected, null, 2)}
+          
+ + )} + + ) +} + +function PageBody() { + return ( +
+
+ +
+
+ ) +} + +export default function RejectedQuotesPage() { + return ( + <> + + Quotes + , + ]} + > + Rejected + + Rejected Quotes + }> + + + + + + + ) +}