Compare commits
27 Commits
653f7dbeeb
...
new-eveal
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b24f433a5 | |||
| dcf50fb8af | |||
| cc4d56ae4c | |||
| 697a08e481 | |||
| 16078cc62b | |||
| 3a35d2181d | |||
| 6d2b5926bb | |||
| cc3bdccd9a | |||
| 3348b9f668 | |||
| d0c198118d | |||
| 2ab3f01d89 | |||
| 3981475c55 | |||
| acde42b406 | |||
| 7ca38aee70 | |||
| dd031551ca | |||
| f3cb4798d5 | |||
| 9cd0d5fb5e | |||
| 5ac369a643 | |||
| b2c97c1327 | |||
| 4ae044dace | |||
| c444f51423 | |||
| a201a95756 | |||
| b32169f433 | |||
| 023693c4c8 | |||
| 47bd728530 | |||
| 7e0ea10d68 | |||
| cd1965acc4 |
+504
-101
@@ -57,7 +57,31 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/RuleBookResponse"
|
$ref: "#/components/schemas/RuleBookResponse"
|
||||||
"400":
|
"400":
|
||||||
description: Invalid request (e.g. blank name)
|
description: |-
|
||||||
|
Returned when:
|
||||||
|
- the request is invalid (e.g. blank name)
|
||||||
|
- the rule book is the default rule book, which cannot be modified
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- rule-book
|
||||||
|
summary: Delete a rule book
|
||||||
|
operationId: deleteRuleBook
|
||||||
|
parameters:
|
||||||
|
- name: ruleBookId
|
||||||
|
in: path
|
||||||
|
description: Id of the rule book
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
responses:
|
||||||
|
"204":
|
||||||
|
description: The rule book was deleted
|
||||||
|
"400":
|
||||||
|
description: |-
|
||||||
|
Returned when:
|
||||||
|
- the rule book is associated to a character
|
||||||
|
- the rule book is the default rule book, which cannot be deleted
|
||||||
/ledgers/main/{ledgerId}:
|
/ledgers/main/{ledgerId}:
|
||||||
put:
|
put:
|
||||||
tags:
|
tags:
|
||||||
@@ -87,7 +111,10 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/MainLedgerResponse"
|
$ref: "#/components/schemas/MainLedgerResponse"
|
||||||
"400":
|
"400":
|
||||||
description: "The ledger is not a main ledger, or the request is invalid"
|
description: |-
|
||||||
|
Returned when:
|
||||||
|
- the ledger is not a main ledger
|
||||||
|
- the request is invalid
|
||||||
"404":
|
"404":
|
||||||
description: No ledger with this id
|
description: No ledger with this id
|
||||||
/ledgers/combined/{ledgerId}:
|
/ledgers/combined/{ledgerId}:
|
||||||
@@ -119,7 +146,10 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/CombinedLedgerResponse"
|
$ref: "#/components/schemas/CombinedLedgerResponse"
|
||||||
"400":
|
"400":
|
||||||
description: "The ledger is not a combined ledger, or the request is invalid"
|
description: |-
|
||||||
|
Returned when:
|
||||||
|
- the ledger is not a combined ledger
|
||||||
|
- the request is invalid
|
||||||
"404":
|
"404":
|
||||||
description: No ledger with this id
|
description: No ledger with this id
|
||||||
/characters/{characterId}/rule-book:
|
/characters/{characterId}/rule-book:
|
||||||
@@ -175,8 +205,12 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/CharacterRuleBookResponse"
|
$ref: "#/components/schemas/CharacterRuleBookResponse"
|
||||||
"400":
|
"400":
|
||||||
description: "The referenced rule book or a bound ledger does not exist,\
|
description: |-
|
||||||
\ or a ledger binding is missing"
|
Returned when:
|
||||||
|
- the referenced rule book does not exist
|
||||||
|
- a bound ledger does not exist
|
||||||
|
- a bound ledger is not a main or system ledger
|
||||||
|
- a required ledger binding is missing
|
||||||
/rule-books:
|
/rule-books:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -207,6 +241,13 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
"201":
|
"201":
|
||||||
description: The created rule book
|
description: The created rule book
|
||||||
|
headers:
|
||||||
|
Location:
|
||||||
|
description: URL of the created rule book
|
||||||
|
style: simple
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
content:
|
content:
|
||||||
'*/*':
|
'*/*':
|
||||||
schema:
|
schema:
|
||||||
@@ -238,6 +279,13 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
"201":
|
"201":
|
||||||
description: The created main ledger
|
description: The created main ledger
|
||||||
|
headers:
|
||||||
|
Location:
|
||||||
|
description: URL of the created main ledger
|
||||||
|
style: simple
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
content:
|
content:
|
||||||
'*/*':
|
'*/*':
|
||||||
schema:
|
schema:
|
||||||
@@ -260,6 +308,13 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
"201":
|
"201":
|
||||||
description: The created combined ledger
|
description: The created combined ledger
|
||||||
|
headers:
|
||||||
|
Location:
|
||||||
|
description: URL of the created combined ledger
|
||||||
|
style: simple
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
content:
|
content:
|
||||||
'*/*':
|
'*/*':
|
||||||
schema:
|
schema:
|
||||||
@@ -295,6 +350,251 @@ paths:
|
|||||||
description: New activities fetched and stored
|
description: New activities fetched and stored
|
||||||
"400":
|
"400":
|
||||||
description: No character with this id
|
description: No character with this id
|
||||||
|
/rule-books/script-definitions:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- rule-book
|
||||||
|
summary: Download the TypeScript definitions for the rule script runtime
|
||||||
|
operationId: getScriptDefinitions
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The rule-runner.d.ts type definitions
|
||||||
|
content:
|
||||||
|
text/plain:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
/market/{marketTypeId}/scan:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- market
|
||||||
|
summary: "Scan a single market type, returning its volume-weighted price quartiles"
|
||||||
|
operationId: scanMarketType
|
||||||
|
parameters:
|
||||||
|
- name: marketTypeId
|
||||||
|
in: path
|
||||||
|
description: The market type id to scan
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
- name: days
|
||||||
|
in: query
|
||||||
|
description: Number of most recent days of history to analyse
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
default: 365
|
||||||
|
minimum: 1
|
||||||
|
- name: brokerFee
|
||||||
|
in: query
|
||||||
|
description: "Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both\
|
||||||
|
\ buy and sell orders"
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
default: 0.015
|
||||||
|
maximum: 1
|
||||||
|
minimum: 0
|
||||||
|
- name: salesTax
|
||||||
|
in: query
|
||||||
|
description: "Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell\
|
||||||
|
\ orders"
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
default: 0.036
|
||||||
|
maximum: 1
|
||||||
|
minimum: 0
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The scan result for the requested market type
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/MarketScanResponse"
|
||||||
|
"400":
|
||||||
|
description: The days parameter is not greater than 0
|
||||||
|
/market/{marketTypeId}/history:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- market
|
||||||
|
summary: "Find the market history of a type, most recent first"
|
||||||
|
operationId: findHistory
|
||||||
|
parameters:
|
||||||
|
- name: marketTypeId
|
||||||
|
in: path
|
||||||
|
description: Id of the market type
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
- name: days
|
||||||
|
in: query
|
||||||
|
description: Optional number of most recent days to return; omit for the full
|
||||||
|
history
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
minimum: 1
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The market history of the type
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/MarketHistoryResponse"
|
||||||
|
"400":
|
||||||
|
description: The days parameter is not greater than 0
|
||||||
|
/market/types:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- market
|
||||||
|
summary: Return the static market type details for each requested type id
|
||||||
|
operationId: findTypes
|
||||||
|
parameters:
|
||||||
|
- name: ids
|
||||||
|
in: query
|
||||||
|
description: "Market type ids to look up, e.g. ids=34,35"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The market types found for the requested ids; unknown ids are
|
||||||
|
omitted
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/MarketTypeResponse"
|
||||||
|
"400":
|
||||||
|
description: |-
|
||||||
|
Returned when:
|
||||||
|
- the ids parameter is missing
|
||||||
|
- an ids value is not a numeric id
|
||||||
|
/market/types/search:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- market
|
||||||
|
summary: "Search marketable types whose name contains the given text, case-insensitively"
|
||||||
|
operationId: searchTypes
|
||||||
|
parameters:
|
||||||
|
- name: name
|
||||||
|
in: query
|
||||||
|
description: "Text to match against the type name, e.g. name=tritan"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
description: "Maximum number of results to return, defaults to 50"
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
default: 50
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "The marketable types matching the search, ordered by name\
|
||||||
|
\ and capped at the requested limit"
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/MarketTypeResponse"
|
||||||
|
"400":
|
||||||
|
description: |-
|
||||||
|
Returned when:
|
||||||
|
- the name parameter is missing
|
||||||
|
- the limit value is not a number
|
||||||
|
/market/scan:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- market
|
||||||
|
summary: "Scan every tracked market type, returning volume-weighted price quartiles\
|
||||||
|
\ for each"
|
||||||
|
operationId: scanMarket
|
||||||
|
parameters:
|
||||||
|
- name: days
|
||||||
|
in: query
|
||||||
|
description: Number of most recent days of history to analyse
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
default: 365
|
||||||
|
minimum: 1
|
||||||
|
- name: brokerFee
|
||||||
|
in: query
|
||||||
|
description: "Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both\
|
||||||
|
\ buy and sell orders"
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
default: 0.015
|
||||||
|
maximum: 1
|
||||||
|
minimum: 0
|
||||||
|
- name: salesTax
|
||||||
|
in: query
|
||||||
|
description: "Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell\
|
||||||
|
\ orders"
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
default: 0.036
|
||||||
|
maximum: 1
|
||||||
|
minimum: 0
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "The scan results, one entry per tracked market type"
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/MarketScanResponse"
|
||||||
|
"400":
|
||||||
|
description: The days parameter is not greater than 0
|
||||||
|
/market/prices:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- market
|
||||||
|
summary: "Return the current Jita order book (highest buy, lowest sell, order\
|
||||||
|
\ count) for each requested market type"
|
||||||
|
operationId: currentPrices
|
||||||
|
parameters:
|
||||||
|
- name: types
|
||||||
|
in: query
|
||||||
|
description: "Market type ids to price, e.g. types=34,35"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "The order book for each requested type, one entry per type"
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/MarketPriceResponse"
|
||||||
|
"400":
|
||||||
|
description: |-
|
||||||
|
Returned when:
|
||||||
|
- the types parameter is missing
|
||||||
|
- a types value is not a numeric id
|
||||||
/ledgers:
|
/ledgers:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -309,9 +609,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
oneOf:
|
$ref: "#/components/schemas/LedgerResponse"
|
||||||
- $ref: "#/components/schemas/CombinedLedgerResponse"
|
|
||||||
- $ref: "#/components/schemas/MainLedgerResponse"
|
|
||||||
/ledgers/{ledgerId}:
|
/ledgers/{ledgerId}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -332,9 +630,7 @@ paths:
|
|||||||
content:
|
content:
|
||||||
'*/*':
|
'*/*':
|
||||||
schema:
|
schema:
|
||||||
oneOf:
|
$ref: "#/components/schemas/LedgerResponse"
|
||||||
- $ref: "#/components/schemas/CombinedLedgerResponse"
|
|
||||||
- $ref: "#/components/schemas/MainLedgerResponse"
|
|
||||||
"400":
|
"400":
|
||||||
description: The ledger cannot be exposed (system ledger)
|
description: The ledger cannot be exposed (system ledger)
|
||||||
"404":
|
"404":
|
||||||
@@ -402,6 +698,21 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/CharacterResponse"
|
$ref: "#/components/schemas/CharacterResponse"
|
||||||
|
/characters/rule-books:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- character-rule-book
|
||||||
|
summary: Find the rule books of all characters that have a token
|
||||||
|
operationId: findAllCharacterRuleBooks
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Rule book assignments of all characters with a token
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/CharacterRuleBookResponse"
|
||||||
/acquisitions:
|
/acquisitions:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -419,54 +730,25 @@ paths:
|
|||||||
$ref: "#/components/schemas/AcquisitionResponse"
|
$ref: "#/components/schemas/AcquisitionResponse"
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
RuleClauseResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
rate:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- NONE
|
|
||||||
- VALUE
|
|
||||||
- JITA_BUY
|
|
||||||
- JITA_SELL
|
|
||||||
- EVE_ESTIMATE
|
|
||||||
fromLedgerRef:
|
|
||||||
type: string
|
|
||||||
pattern: "[a-z]+(-[a-z]+)*"
|
|
||||||
toLedgerRef:
|
|
||||||
type: string
|
|
||||||
pattern: "[a-z]+(-[a-z]+)*"
|
|
||||||
required:
|
|
||||||
- fromLedgerRef
|
|
||||||
- rate
|
|
||||||
- toLedgerRef
|
|
||||||
RuleResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
clauses:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: "#/components/schemas/RuleClauseResponse"
|
|
||||||
required:
|
|
||||||
- clauses
|
|
||||||
UpdateRuleBookRequest:
|
UpdateRuleBookRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
usedForAcquisitions:
|
||||||
|
type: boolean
|
||||||
ledgerRefs:
|
ledgerRefs:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
pattern: "[a-z]+(-[a-z]+)*"
|
pattern: "[a-z][a-zA-Z0-9]*"
|
||||||
rules:
|
script:
|
||||||
type: object
|
type: string
|
||||||
additionalProperties:
|
|
||||||
$ref: "#/components/schemas/RuleResponse"
|
|
||||||
required:
|
required:
|
||||||
- ledgerRefs
|
- ledgerRefs
|
||||||
- name
|
- name
|
||||||
- rules
|
- script
|
||||||
|
- usedForAcquisitions
|
||||||
RuleBookResponse:
|
RuleBookResponse:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -475,20 +757,21 @@ components:
|
|||||||
format: uuid
|
format: uuid
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
usedForAcquisitions:
|
||||||
|
type: boolean
|
||||||
ledgerRefs:
|
ledgerRefs:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
pattern: "[a-z]+(-[a-z]+)*"
|
pattern: "[a-z][a-zA-Z0-9]*"
|
||||||
rules:
|
script:
|
||||||
type: object
|
type: string
|
||||||
additionalProperties:
|
|
||||||
$ref: "#/components/schemas/RuleResponse"
|
|
||||||
required:
|
required:
|
||||||
- ledgerRefs
|
- ledgerRefs
|
||||||
- name
|
- name
|
||||||
- ruleBookId
|
- ruleBookId
|
||||||
- rules
|
- script
|
||||||
|
- usedForAcquisitions
|
||||||
UpdateMainLedgerRequest:
|
UpdateMainLedgerRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -508,10 +791,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
balance:
|
balance:
|
||||||
type: number
|
type: number
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- MAIN
|
|
||||||
required:
|
required:
|
||||||
- balance
|
- balance
|
||||||
- ledgerId
|
- ledgerId
|
||||||
@@ -546,10 +825,6 @@ components:
|
|||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- COMBINED
|
|
||||||
required:
|
required:
|
||||||
- balance
|
- balance
|
||||||
- ledgerId
|
- ledgerId
|
||||||
@@ -569,15 +844,24 @@ components:
|
|||||||
required:
|
required:
|
||||||
- bindings
|
- bindings
|
||||||
- ruleBookId
|
- ruleBookId
|
||||||
CharacterRuleBookResponse:
|
CharacterResponse:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
characterId:
|
characterId:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
ruleBookId:
|
name:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
required:
|
||||||
|
- characterId
|
||||||
|
- name
|
||||||
|
CharacterRuleBookResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
character:
|
||||||
|
$ref: "#/components/schemas/CharacterResponse"
|
||||||
|
ruleBook:
|
||||||
|
$ref: "#/components/schemas/RuleBookSummaryResponse"
|
||||||
bindings:
|
bindings:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
@@ -585,26 +869,38 @@ components:
|
|||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
- bindings
|
- bindings
|
||||||
- characterId
|
- character
|
||||||
|
- ruleBook
|
||||||
|
RuleBookSummaryResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
ruleBookId:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
- ruleBookId
|
- ruleBookId
|
||||||
CreateRuleBookRequest:
|
CreateRuleBookRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
usedForAcquisitions:
|
||||||
|
type: boolean
|
||||||
ledgerRefs:
|
ledgerRefs:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
pattern: "[a-z]+(-[a-z]+)*"
|
pattern: "[a-z][a-zA-Z0-9]*"
|
||||||
rules:
|
script:
|
||||||
type: object
|
type: string
|
||||||
additionalProperties:
|
|
||||||
$ref: "#/components/schemas/RuleResponse"
|
|
||||||
required:
|
required:
|
||||||
- ledgerRefs
|
- ledgerRefs
|
||||||
- name
|
- name
|
||||||
- rules
|
- script
|
||||||
|
- usedForAcquisitions
|
||||||
CreateMainLedgerRequest:
|
CreateMainLedgerRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -625,16 +921,140 @@ components:
|
|||||||
required:
|
required:
|
||||||
- memberLedgerIds
|
- memberLedgerIds
|
||||||
- name
|
- name
|
||||||
LedgerResponse:
|
MarketScanResponse:
|
||||||
type: object
|
type: object
|
||||||
|
properties:
|
||||||
|
marketTypeId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
buy:
|
||||||
|
type: number
|
||||||
|
sell:
|
||||||
|
type: number
|
||||||
|
q1:
|
||||||
|
type: number
|
||||||
|
median:
|
||||||
|
type: number
|
||||||
|
q3:
|
||||||
|
type: number
|
||||||
|
totalVolume:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
profit:
|
||||||
|
type: number
|
||||||
|
score:
|
||||||
|
type: number
|
||||||
|
required:
|
||||||
|
- buy
|
||||||
|
- marketTypeId
|
||||||
|
- median
|
||||||
|
- profit
|
||||||
|
- q1
|
||||||
|
- q3
|
||||||
|
- score
|
||||||
|
- sell
|
||||||
|
- totalVolume
|
||||||
|
MarketHistoryResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
marketTypeId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
average:
|
||||||
|
type: number
|
||||||
|
highest:
|
||||||
|
type: number
|
||||||
|
lowest:
|
||||||
|
type: number
|
||||||
|
orderCount:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
volume:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required:
|
||||||
|
- average
|
||||||
|
- date
|
||||||
|
- highest
|
||||||
|
- lowest
|
||||||
|
- marketTypeId
|
||||||
|
- orderCount
|
||||||
|
- volume
|
||||||
|
MarketTypeResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
groupId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
marketGroupId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
published:
|
||||||
|
type: boolean
|
||||||
|
basePrice:
|
||||||
|
type: number
|
||||||
|
volume:
|
||||||
|
type: number
|
||||||
|
format: double
|
||||||
|
portionSize:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
iconId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required:
|
||||||
|
- basePrice
|
||||||
|
- description
|
||||||
|
- groupId
|
||||||
|
- iconId
|
||||||
|
- id
|
||||||
|
- marketGroupId
|
||||||
|
- name
|
||||||
|
- portionSize
|
||||||
|
- published
|
||||||
|
- volume
|
||||||
|
MarketPriceResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
marketTypeId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
buy:
|
||||||
|
type: number
|
||||||
|
sell:
|
||||||
|
type: number
|
||||||
|
orderCount:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required:
|
||||||
|
- buy
|
||||||
|
- marketTypeId
|
||||||
|
- orderCount
|
||||||
|
- sell
|
||||||
|
LedgerResponse:
|
||||||
discriminator:
|
discriminator:
|
||||||
propertyName: type
|
propertyName: type
|
||||||
|
mapping:
|
||||||
|
MAIN: "#/components/schemas/MainLedgerResponse"
|
||||||
|
COMBINED: "#/components/schemas/CombinedLedgerResponse"
|
||||||
|
oneOf:
|
||||||
|
- $ref: "#/components/schemas/MainLedgerResponse"
|
||||||
|
- $ref: "#/components/schemas/CombinedLedgerResponse"
|
||||||
properties:
|
properties:
|
||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
required:
|
||||||
- MAIN
|
- type
|
||||||
- COMBINED
|
|
||||||
IskTransferResponse:
|
IskTransferResponse:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: "#/components/schemas/TransferResponse"
|
- $ref: "#/components/schemas/TransferResponse"
|
||||||
@@ -648,10 +1068,6 @@ components:
|
|||||||
format: uuid
|
format: uuid
|
||||||
amount:
|
amount:
|
||||||
type: number
|
type: number
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- ISK
|
|
||||||
required:
|
required:
|
||||||
- amount
|
- amount
|
||||||
- fromLedgerId
|
- fromLedgerId
|
||||||
@@ -673,10 +1089,6 @@ components:
|
|||||||
quantity:
|
quantity:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- ITEM
|
|
||||||
required:
|
required:
|
||||||
- fromLedgerId
|
- fromLedgerId
|
||||||
- marketTypeId
|
- marketTypeId
|
||||||
@@ -699,9 +1111,7 @@ components:
|
|||||||
transfers:
|
transfers:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
oneOf:
|
$ref: "#/components/schemas/TransferResponse"
|
||||||
- $ref: "#/components/schemas/IskTransferResponse"
|
|
||||||
- $ref: "#/components/schemas/ItemTransferResponse"
|
|
||||||
required:
|
required:
|
||||||
- characterId
|
- characterId
|
||||||
- datetime
|
- datetime
|
||||||
@@ -709,15 +1119,19 @@ components:
|
|||||||
- transactionId
|
- transactionId
|
||||||
- transfers
|
- transfers
|
||||||
TransferResponse:
|
TransferResponse:
|
||||||
type: object
|
|
||||||
discriminator:
|
discriminator:
|
||||||
propertyName: type
|
propertyName: type
|
||||||
|
mapping:
|
||||||
|
ISK: "#/components/schemas/IskTransferResponse"
|
||||||
|
ITEM: "#/components/schemas/ItemTransferResponse"
|
||||||
|
oneOf:
|
||||||
|
- $ref: "#/components/schemas/IskTransferResponse"
|
||||||
|
- $ref: "#/components/schemas/ItemTransferResponse"
|
||||||
properties:
|
properties:
|
||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
required:
|
||||||
- ISK
|
- type
|
||||||
- ITEM
|
|
||||||
BalanceResponse:
|
BalanceResponse:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -742,17 +1156,6 @@ components:
|
|||||||
required:
|
required:
|
||||||
- quantity
|
- quantity
|
||||||
- typeId
|
- typeId
|
||||||
CharacterResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
characterId:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- characterId
|
|
||||||
- name
|
|
||||||
AcquisitionResponse:
|
AcquisitionResponse:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
Generated
+199
-170
@@ -14,10 +14,10 @@
|
|||||||
"@vueuse/integrations": "^14.3.0",
|
"@vueuse/integrations": "^14.3.0",
|
||||||
"@vueuse/router": "^14.3.0",
|
"@vueuse/router": "^14.3.0",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"axios-rate-limit": "^1.3.1",
|
|
||||||
"gemory": "file:",
|
"gemory": "file:",
|
||||||
"loglevel": "^1.8.1",
|
"loglevel": "^1.8.1",
|
||||||
"loglevel-plugin-prefix": "^0.8.4",
|
"loglevel-plugin-prefix": "^0.8.4",
|
||||||
|
"monaco-editor": "^0.55.1",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"sortablejs": "^1.15.7",
|
"sortablejs": "^1.15.7",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
@@ -230,13 +230,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
|
||||||
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
|
"integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tybys/wasm-util": "^0.10.1"
|
"@tybys/wasm-util": "^0.10.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -532,49 +532,49 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/node": {
|
"node_modules/@tailwindcss/node": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz",
|
||||||
"integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==",
|
"integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/remapping": "^2.3.5",
|
"@jridgewell/remapping": "^2.3.5",
|
||||||
"enhanced-resolve": "^5.21.0",
|
"enhanced-resolve": "5.21.6",
|
||||||
"jiti": "^2.6.1",
|
"jiti": "^2.7.0",
|
||||||
"lightningcss": "1.32.0",
|
"lightningcss": "1.32.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"source-map-js": "^1.2.1",
|
"source-map-js": "^1.2.1",
|
||||||
"tailwindcss": "4.3.0"
|
"tailwindcss": "4.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide": {
|
"node_modules/@tailwindcss/oxide": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz",
|
||||||
"integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==",
|
"integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 20"
|
"node": ">= 20"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tailwindcss/oxide-android-arm64": "4.3.0",
|
"@tailwindcss/oxide-android-arm64": "4.3.1",
|
||||||
"@tailwindcss/oxide-darwin-arm64": "4.3.0",
|
"@tailwindcss/oxide-darwin-arm64": "4.3.1",
|
||||||
"@tailwindcss/oxide-darwin-x64": "4.3.0",
|
"@tailwindcss/oxide-darwin-x64": "4.3.1",
|
||||||
"@tailwindcss/oxide-freebsd-x64": "4.3.0",
|
"@tailwindcss/oxide-freebsd-x64": "4.3.1",
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0",
|
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1",
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.3.0",
|
"@tailwindcss/oxide-linux-arm64-gnu": "4.3.1",
|
||||||
"@tailwindcss/oxide-linux-arm64-musl": "4.3.0",
|
"@tailwindcss/oxide-linux-arm64-musl": "4.3.1",
|
||||||
"@tailwindcss/oxide-linux-x64-gnu": "4.3.0",
|
"@tailwindcss/oxide-linux-x64-gnu": "4.3.1",
|
||||||
"@tailwindcss/oxide-linux-x64-musl": "4.3.0",
|
"@tailwindcss/oxide-linux-x64-musl": "4.3.1",
|
||||||
"@tailwindcss/oxide-wasm32-wasi": "4.3.0",
|
"@tailwindcss/oxide-wasm32-wasi": "4.3.1",
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.3.0",
|
"@tailwindcss/oxide-win32-arm64-msvc": "4.3.1",
|
||||||
"@tailwindcss/oxide-win32-x64-msvc": "4.3.0"
|
"@tailwindcss/oxide-win32-x64-msvc": "4.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz",
|
||||||
"integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==",
|
"integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -589,9 +589,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz",
|
||||||
"integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==",
|
"integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -606,9 +606,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz",
|
||||||
"integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==",
|
"integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -623,9 +623,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz",
|
||||||
"integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==",
|
"integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -640,9 +640,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz",
|
||||||
"integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==",
|
"integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -657,9 +657,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz",
|
||||||
"integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==",
|
"integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -677,9 +677,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz",
|
||||||
"integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==",
|
"integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -697,9 +697,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz",
|
||||||
"integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==",
|
"integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -717,9 +717,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz",
|
||||||
"integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==",
|
"integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -737,9 +737,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz",
|
||||||
"integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==",
|
"integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==",
|
||||||
"bundleDependencies": [
|
"bundleDependencies": [
|
||||||
"@napi-rs/wasm-runtime",
|
"@napi-rs/wasm-runtime",
|
||||||
"@emnapi/core",
|
"@emnapi/core",
|
||||||
@@ -759,7 +759,7 @@
|
|||||||
"@emnapi/runtime": "^1.10.0",
|
"@emnapi/runtime": "^1.10.0",
|
||||||
"@emnapi/wasi-threads": "^1.2.1",
|
"@emnapi/wasi-threads": "^1.2.1",
|
||||||
"@napi-rs/wasm-runtime": "^1.1.4",
|
"@napi-rs/wasm-runtime": "^1.1.4",
|
||||||
"@tybys/wasm-util": "^0.10.1",
|
"@tybys/wasm-util": "^0.10.2",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -767,9 +767,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz",
|
||||||
"integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==",
|
"integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -784,9 +784,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz",
|
||||||
"integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==",
|
"integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -801,15 +801,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tailwindcss/vite": {
|
"node_modules/@tailwindcss/vite": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz",
|
||||||
"integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==",
|
"integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/node": "4.3.0",
|
"@tailwindcss/node": "4.3.1",
|
||||||
"@tailwindcss/oxide": "4.3.0",
|
"@tailwindcss/oxide": "4.3.1",
|
||||||
"tailwindcss": "4.3.0"
|
"tailwindcss": "4.3.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vite": "^5.2.0 || ^6 || ^7 || ^8"
|
"vite": "^5.2.0 || ^6 || ^7 || ^8"
|
||||||
@@ -857,15 +857,22 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "25.9.1",
|
"version": "25.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz",
|
||||||
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
"integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": ">=7.24.0 <7.24.7"
|
"undici-types": ">=7.24.0 <7.24.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@types/web-bluetooth": {
|
"node_modules/@types/web-bluetooth": {
|
||||||
"version": "0.0.21",
|
"version": "0.0.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
||||||
@@ -1059,13 +1066,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-core": {
|
"node_modules/@vue/compiler-core": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.38.tgz",
|
||||||
"integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==",
|
"integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.3",
|
"@babel/parser": "^7.29.7",
|
||||||
"@vue/shared": "3.5.35",
|
"@vue/shared": "3.5.38",
|
||||||
"entities": "^7.0.1",
|
"entities": "^7.0.1",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
@@ -1078,26 +1085,26 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-dom": {
|
"node_modules/@vue/compiler-dom": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz",
|
||||||
"integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==",
|
"integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.5.35",
|
"@vue/compiler-core": "3.5.38",
|
||||||
"@vue/shared": "3.5.35"
|
"@vue/shared": "3.5.38"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-sfc": {
|
"node_modules/@vue/compiler-sfc": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz",
|
||||||
"integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==",
|
"integrity": "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.3",
|
"@babel/parser": "^7.29.7",
|
||||||
"@vue/compiler-core": "3.5.35",
|
"@vue/compiler-core": "3.5.38",
|
||||||
"@vue/compiler-dom": "3.5.35",
|
"@vue/compiler-dom": "3.5.38",
|
||||||
"@vue/compiler-ssr": "3.5.35",
|
"@vue/compiler-ssr": "3.5.38",
|
||||||
"@vue/shared": "3.5.35",
|
"@vue/shared": "3.5.38",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"postcss": "^8.5.15",
|
"postcss": "^8.5.15",
|
||||||
@@ -1111,13 +1118,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-ssr": {
|
"node_modules/@vue/compiler-ssr": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz",
|
||||||
"integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==",
|
"integrity": "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.35",
|
"@vue/compiler-dom": "3.5.38",
|
||||||
"@vue/shared": "3.5.35"
|
"@vue/shared": "3.5.38"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/devtools-api": {
|
"node_modules/@vue/devtools-api": {
|
||||||
@@ -1154,9 +1161,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/language-core": {
|
"node_modules/@vue/language-core": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.5.tgz",
|
||||||
"integrity": "sha512-X6p+7nfY7vVT6dQwUJ+v0Jfq/lwIfhL2jMi91dQ3ln4hnlGXlxsDu/FNkeyHYgvYtyQy18ZX76IZy7X4diDbiQ==",
|
"integrity": "sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1170,53 +1177,53 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/reactivity": {
|
"node_modules/@vue/reactivity": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.38.tgz",
|
||||||
"integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==",
|
"integrity": "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.5.35"
|
"@vue/shared": "3.5.38"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-core": {
|
"node_modules/@vue/runtime-core": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.38.tgz",
|
||||||
"integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==",
|
"integrity": "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.35",
|
"@vue/reactivity": "3.5.38",
|
||||||
"@vue/shared": "3.5.35"
|
"@vue/shared": "3.5.38"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-dom": {
|
"node_modules/@vue/runtime-dom": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz",
|
||||||
"integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==",
|
"integrity": "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.35",
|
"@vue/reactivity": "3.5.38",
|
||||||
"@vue/runtime-core": "3.5.35",
|
"@vue/runtime-core": "3.5.38",
|
||||||
"@vue/shared": "3.5.35",
|
"@vue/shared": "3.5.38",
|
||||||
"csstype": "^3.2.3"
|
"csstype": "^3.2.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/server-renderer": {
|
"node_modules/@vue/server-renderer": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.38.tgz",
|
||||||
"integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==",
|
"integrity": "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.5.35",
|
"@vue/compiler-ssr": "3.5.38",
|
||||||
"@vue/shared": "3.5.35"
|
"@vue/shared": "3.5.38"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "3.5.35"
|
"vue": "3.5.38"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/shared": {
|
"node_modules/@vue/shared": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.38.tgz",
|
||||||
"integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==",
|
"integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vueuse/components": {
|
"node_modules/@vueuse/components": {
|
||||||
@@ -1353,9 +1360,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.16.0",
|
"version": "8.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
|
||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@@ -1433,9 +1440,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.16.1",
|
"version": "1.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz",
|
||||||
"integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
|
"integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.16.0",
|
"follow-redirects": "^1.16.0",
|
||||||
@@ -1444,18 +1451,6 @@
|
|||||||
"proxy-from-env": "^2.1.0"
|
"proxy-from-env": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios-rate-limit": {
|
|
||||||
"version": "1.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios-rate-limit/-/axios-rate-limit-1.9.0.tgz",
|
|
||||||
"integrity": "sha512-p79ltBSUF+8i4uLYr1lu8s28chIaQ8x/oiV3Vb6NDkNAqkKD/AzTAchRPEXK+weRyAkRTIa/GowKcKDn1BHRPQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"axios": ">=0.18.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"axios": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/birpc": {
|
"node_modules/birpc": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
|
||||||
@@ -1585,6 +1580,15 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
|
||||||
|
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -1600,9 +1604,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.22.1",
|
"version": "5.21.6",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz",
|
||||||
"integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==",
|
"integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1741,16 +1745,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
|
||||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
"es-set-tostringtag": "^2.1.0",
|
"es-set-tostringtag": "^2.1.0",
|
||||||
"hasown": "^2.0.2",
|
"hasown": "^2.0.4",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.35"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
@@ -2265,6 +2269,18 @@
|
|||||||
"url": "https://github.com/sponsors/sxzz"
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "14.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
|
||||||
|
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -2330,6 +2346,16 @@
|
|||||||
"pathe": "^2.0.1"
|
"pathe": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.55.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
||||||
|
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dompurify": "3.2.7",
|
||||||
|
"marked": "14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -2361,15 +2387,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/obug": {
|
"node_modules/obug": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz",
|
||||||
"integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
|
"integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://github.com/sponsors/sxzz",
|
"https://github.com/sponsors/sxzz",
|
||||||
"https://opencollective.com/debug"
|
"https://opencollective.com/debug"
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-browserify": {
|
"node_modules/path-browserify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -2610,9 +2639,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz",
|
||||||
"integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==",
|
"integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -2738,9 +2767,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.0.15",
|
"version": "8.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
|
||||||
"integrity": "sha512-qpgllRxrLqwsMAGRdLhsEr9bepaOQk1rxH1xT2coBXLaEB/bfkqQj1j7RMxwMfnYrvO1ZnFMiwX+wBVgnsyn0g==",
|
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2926,16 +2955,16 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vue": {
|
"node_modules/vue": {
|
||||||
"version": "3.5.35",
|
"version": "3.5.38",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.38.tgz",
|
||||||
"integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==",
|
"integrity": "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.35",
|
"@vue/compiler-dom": "3.5.38",
|
||||||
"@vue/compiler-sfc": "3.5.35",
|
"@vue/compiler-sfc": "3.5.38",
|
||||||
"@vue/runtime-dom": "3.5.35",
|
"@vue/runtime-dom": "3.5.38",
|
||||||
"@vue/server-renderer": "3.5.35",
|
"@vue/server-renderer": "3.5.38",
|
||||||
"@vue/shared": "3.5.35"
|
"@vue/shared": "3.5.38"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
@@ -3029,14 +3058,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vue-tsc": {
|
"node_modules/vue-tsc": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.5.tgz",
|
||||||
"integrity": "sha512-SWUEG7YRUeDJHT7Xsuhf02elYX2gxPzzAII7OxDAh4KNOr4QHQ0Lls0YfnaO5GNd560CwVa2HTfdqmA5MqvRqQ==",
|
"integrity": "sha512-Rzh/G2MmNlMSAMTiQEjDrsb4dgB/jbtEM47rVN2NtidF1dfb/q4w4QvpQBtW5+y3y5H27Hjh7deVwk+YB02fNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@volar/typescript": "2.4.28",
|
"@volar/typescript": "2.4.28",
|
||||||
"@vue/language-core": "3.3.3"
|
"@vue/language-core": "3.3.5"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vue-tsc": "bin/vue-tsc.js"
|
"vue-tsc": "bin/vue-tsc.js"
|
||||||
|
|||||||
+1
-1
@@ -16,10 +16,10 @@
|
|||||||
"@vueuse/integrations": "^14.3.0",
|
"@vueuse/integrations": "^14.3.0",
|
||||||
"@vueuse/router": "^14.3.0",
|
"@vueuse/router": "^14.3.0",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"axios-rate-limit": "^1.3.1",
|
|
||||||
"gemory": "file:",
|
"gemory": "file:",
|
||||||
"loglevel": "^1.8.1",
|
"loglevel": "^1.8.1",
|
||||||
"loglevel-plugin-prefix": "^0.8.4",
|
"loglevel-plugin-prefix": "^0.8.4",
|
||||||
|
"monaco-editor": "^0.55.1",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"sortablejs": "^1.15.7",
|
"sortablejs": "^1.15.7",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import {computed} from 'vue';
|
import {computed} from 'vue';
|
||||||
import {RouterView, useRoute} from 'vue-router';
|
import {RouterView, useRoute} from 'vue-router';
|
||||||
import {Sidebar} from './sidebar';
|
import {Sidebar} from './sidebar';
|
||||||
|
import {ConfirmModal} from '@/confirm';
|
||||||
import {routeNames} from '@/routes';
|
import {routeNames} from '@/routes';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -21,6 +22,7 @@ const hideSidebar = computed(() => {
|
|||||||
<RouterView />
|
<RouterView />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<ConfirmModal />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ const modelValue = defineModel({ default: false });
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@reference "@/style.css";
|
@reference "@/style.css";
|
||||||
input:checked ~ span:last-child {
|
|
||||||
--tw-translate-x: 1.25rem;
|
input:checked ~ span:last-child {
|
||||||
}
|
transform: translateX(1.25rem);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {computed} from "vue";
|
||||||
|
import {Modal} from "@/components";
|
||||||
|
import {useConfirmStore} from "./useConfirm";
|
||||||
|
|
||||||
|
const confirmStore = useConfirmStore();
|
||||||
|
|
||||||
|
const modalOpen = computed({
|
||||||
|
get: () => confirmStore.open,
|
||||||
|
set: value => {
|
||||||
|
if (!value) {
|
||||||
|
confirmStore.cancel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal v-model:open="modalOpen">
|
||||||
|
<div class="bg-slate-800 rounded pb-4 w-96">
|
||||||
|
<span class="m-2">{{ confirmStore.options.title ?? "Confirm" }}</span>
|
||||||
|
<hr />
|
||||||
|
<div class="m-4">{{ confirmStore.options.message }}</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button class="me-2" @click="confirmStore.cancel">{{ confirmStore.options.cancelLabel ?? "Cancel" }}</button>
|
||||||
|
<button class="confirm me-4" :class="confirmStore.options.danger ? 'danger' : ''" @click="confirmStore.accept">{{ confirmStore.options.confirmLabel ?? "Confirm" }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@reference "@/style.css";
|
||||||
|
|
||||||
|
button.confirm {
|
||||||
|
@apply border-emerald-500 bg-emerald-500 hover:bg-emerald-600;
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
@apply border-amber-900 bg-amber-900 hover:bg-amber-800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export { default as ConfirmModal } from './ConfirmModal.vue';
|
||||||
|
export { confirm, useConfirmStore } from './useConfirm';
|
||||||
|
export type { ConfirmOptions } from './useConfirm';
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import {defineStore} from "pinia";
|
||||||
|
import {ref} from "vue";
|
||||||
|
|
||||||
|
export interface ConfirmOptions {
|
||||||
|
title?: string;
|
||||||
|
message: string;
|
||||||
|
confirmLabel?: string;
|
||||||
|
cancelLabel?: string;
|
||||||
|
danger?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useConfirmStore = defineStore('confirm', () => {
|
||||||
|
const open = ref(false);
|
||||||
|
const options = ref<ConfirmOptions>({message: ""});
|
||||||
|
let resolver: ((value: boolean) => void) | undefined;
|
||||||
|
|
||||||
|
const settle = (value: boolean) => {
|
||||||
|
open.value = false;
|
||||||
|
resolver?.(value);
|
||||||
|
resolver = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirm = (opts: ConfirmOptions | string): Promise<boolean> => {
|
||||||
|
options.value = typeof opts === "string" ? {message: opts} : opts;
|
||||||
|
open.value = true;
|
||||||
|
return new Promise<boolean>(resolve => {
|
||||||
|
resolver = resolve;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const accept = () => settle(true);
|
||||||
|
const cancel = () => settle(false);
|
||||||
|
|
||||||
|
return {open, options, confirm, accept, cancel};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const confirm = (opts: ConfirmOptions | string): Promise<boolean> => useConfirmStore().confirm(opts);
|
||||||
+756
-88
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {isCombined, Ledger, LedgerType, LedgerTypes, useLedgersStore} from "./ledger";
|
import {isCombined, Ledger, LedgerType, LedgerTypes, useLedgersStore} from "./ledger";
|
||||||
import {Modal} from "@/components";
|
import {Modal} from "@/components";
|
||||||
import LedgerLabel from "./LedgerLabel.vue";
|
import LedgerLabel from "./LedgerLabel.vue";
|
||||||
@@ -15,8 +14,6 @@ interface Props {
|
|||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const ledgersStore = useLedgersStore();
|
const ledgersStore = useLedgersStore();
|
||||||
const {ledgers} = storeToRefs(ledgersStore);
|
|
||||||
const {findById, findAllById, createMain, createCombined, updateMain, updateCombined} = ledgersStore;
|
|
||||||
|
|
||||||
const modalOpen = ref<boolean>(false);
|
const modalOpen = ref<boolean>(false);
|
||||||
|
|
||||||
@@ -24,7 +21,7 @@ const type = ref<LedgerType>(LedgerTypes.Main);
|
|||||||
const name = ref("");
|
const name = ref("");
|
||||||
const members = ref<Ledger[]>([]);
|
const members = ref<Ledger[]>([]);
|
||||||
const selectedLedger = ref<Ledger>();
|
const selectedLedger = ref<Ledger>();
|
||||||
const availableLedgers = computed(() => ledgers.value
|
const availableLedgers = computed(() => ledgersStore.ledgers
|
||||||
.filter(l => l.ledgerId !== props.ledgerId)
|
.filter(l => l.ledgerId !== props.ledgerId)
|
||||||
.filter(l => !members.value.includes(l)));
|
.filter(l => !members.value.includes(l)));
|
||||||
|
|
||||||
@@ -37,12 +34,12 @@ const addMember = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const open = () => {
|
const open = () => {
|
||||||
const ledger = isCreating.value ? undefined : findById(props.ledgerId);
|
const ledger = isCreating.value ? undefined : ledgersStore.findById(props.ledgerId);
|
||||||
|
|
||||||
if (ledger) {
|
if (ledger) {
|
||||||
type.value = ledger.type;
|
type.value = ledger.type;
|
||||||
name.value = ledger.name;
|
name.value = ledger.name;
|
||||||
members.value = isCombined(ledger) ? findAllById(ledger.memberLedgerIds) : [];
|
members.value = isCombined(ledger) ? ledgersStore.findAllById(ledger.memberLedgerIds) : [];
|
||||||
} else {
|
} else {
|
||||||
type.value = LedgerTypes.Main;
|
type.value = LedgerTypes.Main;
|
||||||
name.value = "";
|
name.value = "";
|
||||||
@@ -62,17 +59,17 @@ const title = computed(() => {
|
|||||||
|
|
||||||
const create = () => {
|
const create = () => {
|
||||||
if (type.value === LedgerTypes.Main) {
|
if (type.value === LedgerTypes.Main) {
|
||||||
createMain({name: name.value})
|
ledgersStore.createMain({name: name.value})
|
||||||
} else {
|
} else {
|
||||||
createCombined({name: name.value, memberLedgerIds: members.value.map(l => l.ledgerId)})
|
ledgersStore.createCombined({name: name.value, memberLedgerIds: members.value.map(l => l.ledgerId)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = () => {
|
const update = () => {
|
||||||
if (type.value === LedgerTypes.Main) {
|
if (type.value === LedgerTypes.Main) {
|
||||||
updateMain(props.ledgerId, {name: name.value})
|
ledgersStore.updateMain(props.ledgerId, {name: name.value})
|
||||||
} else {
|
} else {
|
||||||
updateCombined(props.ledgerId, {name: name.value, memberLedgerIds: members.value.map(l => l.ledgerId)})
|
ledgersStore.updateCombined(props.ledgerId, {name: name.value, memberLedgerIds: members.value.map(l => l.ledgerId)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {Ledger, systemLedger, useLedgersStore} from "@/ledger/ledger.ts";
|
import {Ledger, systemLedger, useLedgersStore} from "@/ledger/ledger.ts";
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {computed} from "vue";
|
import {computed} from "vue";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -9,9 +8,9 @@ interface Props {
|
|||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
const ledger = defineModel<Ledger>();
|
const ledger = defineModel<Ledger>();
|
||||||
const {ledgers: allLedgers} = storeToRefs(useLedgersStore());
|
const ledgersStore = useLedgersStore();
|
||||||
|
|
||||||
const ledgersToUse = computed(() => props.ledgers || allLedgers);
|
const ledgersToUse = computed(() => props.ledgers || ledgersStore.ledgers);
|
||||||
const ledgerId = computed({
|
const ledgerId = computed({
|
||||||
get: () => ledger.value?.ledgerId,
|
get: () => ledger.value?.ledgerId,
|
||||||
set: value => ledger.value = ledgersToUse.value.find(l => l.ledgerId === value)
|
set: value => ledger.value = ledgersToUse.value.find(l => l.ledgerId === value)
|
||||||
|
|||||||
+10
-9
@@ -1,12 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
BalanceResponse,
|
BalanceResponse,
|
||||||
CombinedLedgerResponse,
|
CombinedLedgerResponse,
|
||||||
CombinedLedgerResponseTypeEnum,
|
|
||||||
CreateCombinedLedgerRequest,
|
CreateCombinedLedgerRequest,
|
||||||
CreateMainLedgerRequest,
|
CreateMainLedgerRequest,
|
||||||
LedgerResponseTypeEnum,
|
LedgerResponse,
|
||||||
MainLedgerResponse,
|
MainLedgerResponse,
|
||||||
MainLedgerResponseTypeEnum,
|
|
||||||
TransactionResponse,
|
TransactionResponse,
|
||||||
UpdateCombinedLedgerRequest,
|
UpdateCombinedLedgerRequest,
|
||||||
UpdateMainLedgerRequest
|
UpdateMainLedgerRequest
|
||||||
@@ -16,11 +14,14 @@ import {computed, ref, triggerRef} from "vue";
|
|||||||
import {ledgerApi, transactionApi} from "@/mammon";
|
import {ledgerApi, transactionApi} from "@/mammon";
|
||||||
import {useRouteParams} from "@vueuse/router";
|
import {useRouteParams} from "@vueuse/router";
|
||||||
|
|
||||||
export const LedgerTypes = LedgerResponseTypeEnum;
|
export const LedgerTypes = {
|
||||||
|
Main: 'MAIN',
|
||||||
|
Combined: 'COMBINED',
|
||||||
|
};
|
||||||
|
|
||||||
export type LedgerType = LedgerResponseTypeEnum;
|
export type LedgerType = LedgerResponse['type'];
|
||||||
export type MainLedger = MainLedgerResponse & {type: MainLedgerResponseTypeEnum}
|
export type MainLedger = MainLedgerResponse
|
||||||
export type CombinedLedger = CombinedLedgerResponse & {type: CombinedLedgerResponseTypeEnum}
|
export type CombinedLedger = CombinedLedgerResponse
|
||||||
export type Ledger = MainLedger | CombinedLedger;
|
export type Ledger = MainLedger | CombinedLedger;
|
||||||
|
|
||||||
export const systemLedgerRef = 'system';
|
export const systemLedgerRef = 'system';
|
||||||
@@ -79,9 +80,9 @@ export const findAllTransactionInLeger = (ledger: Ledger | string): Promise<Tran
|
|||||||
export const getLedgerBalance = (ledger: Ledger | string): Promise<BalanceResponse> => ledgerApi.findBalanceByLedgerId(getLedgerId(ledger)).then(response => response.data)
|
export const getLedgerBalance = (ledger: Ledger | string): Promise<BalanceResponse> => ledgerApi.findBalanceByLedgerId(getLedgerId(ledger)).then(response => response.data)
|
||||||
|
|
||||||
export const useLedgerParam = () => {
|
export const useLedgerParam = () => {
|
||||||
const {findById} = useLedgersStore();
|
const ledgersStore = useLedgersStore();
|
||||||
const ledgerId = useRouteParams<string, string>('ledgerId', '', { transform: v => typeof v === 'string' ? v : v[0]});
|
const ledgerId = useRouteParams<string, string>('ledgerId', '', { transform: v => typeof v === 'string' ? v : v[0]});
|
||||||
const ledger = computed(() => findById(ledgerId.value))
|
const ledger = computed(() => ledgersStore.findById(ledgerId.value))
|
||||||
|
|
||||||
return {ledgerId, ledger};
|
return {ledgerId, ledger};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
CharacterApi,
|
CharacterApi,
|
||||||
CharacterRuleBookApi,
|
CharacterRuleBookApi,
|
||||||
LedgerApi,
|
LedgerApi,
|
||||||
|
MarketApi,
|
||||||
ProcessingApi,
|
ProcessingApi,
|
||||||
RuleBookApi,
|
RuleBookApi,
|
||||||
TransactionApi
|
TransactionApi
|
||||||
@@ -31,3 +32,4 @@ export const characterRuleBookApi = new CharacterRuleBookApi(undefined, mammonUr
|
|||||||
export const activityApi = new ActivityApi(undefined, mammonUrl, mammonAxiosInstance);
|
export const activityApi = new ActivityApi(undefined, mammonUrl, mammonAxiosInstance);
|
||||||
export const processingApi = new ProcessingApi(undefined, mammonUrl, mammonAxiosInstance);
|
export const processingApi = new ProcessingApi(undefined, mammonUrl, mammonAxiosInstance);
|
||||||
export const acquisitionApi = new AcquisitionApi(undefined, mammonUrl, mammonAxiosInstance);
|
export const acquisitionApi = new AcquisitionApi(undefined, mammonUrl, mammonAxiosInstance);
|
||||||
|
export const marketApi = new MarketApi(undefined, mammonUrl, mammonAxiosInstance);
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
import { defineStore } from 'pinia';
|
import {defineStore} from 'pinia';
|
||||||
import { RegionalMarketCache } from '../RegionalMarketCache';
|
import {RegionalMarketCache} from '../RegionalMarketCache';
|
||||||
import { jitaId } from '../market';
|
import {jitaId} from '../market';
|
||||||
import { MarketType } from "../type";
|
import {MarketType} from "../type";
|
||||||
import { MarketTypePrice } from './MarketTypePrice';
|
import {MarketTypePrice} from './MarketTypePrice';
|
||||||
import { getEvepraisalPrices } from './evepraisal';
|
import {getMammonPrices} from './mammon';
|
||||||
import { getfuzzworkPrices } from './fuzzwork';
|
|
||||||
|
|
||||||
const cacheDuration = 1000 * 60 * 5; // 5 minutes
|
const CACHE_DURATION = 1000 * 60 * 5; // 5 minutes
|
||||||
const priceGetters = {
|
const BATCH_SIZE = 100;
|
||||||
evepraisal: getEvepraisalPrices,
|
|
||||||
fuzzwork: getfuzzworkPrices
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useApraisalStore = defineStore('appraisal', () => {
|
export const useApraisalStore = defineStore('appraisal', () => {
|
||||||
const cache: RegionalMarketCache<MarketTypePrice> = new RegionalMarketCache(cacheDuration);
|
const cache: RegionalMarketCache<MarketTypePrice> = new RegionalMarketCache(CACHE_DURATION);
|
||||||
|
|
||||||
const getPricesUncached = priceGetters.fuzzwork;
|
const getPricesUncached = getMammonPrices;
|
||||||
|
|
||||||
const getPrice = async (type: MarketType, regionId?: number): Promise<MarketTypePrice> => (await getPrices([type], regionId))[0];
|
const getPrice = async (type: MarketType, regionId?: number): Promise<MarketTypePrice> => (await getPrices([type], regionId))[0];
|
||||||
const getPrices = async (types: MarketType[], regionId?: number): Promise<MarketTypePrice[]> => {
|
const getPrices = async (types: MarketType[], regionId?: number): Promise<MarketTypePrice[]> => {
|
||||||
@@ -34,7 +30,13 @@ export const useApraisalStore = defineStore('appraisal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (uncached.length > 0) {
|
if (uncached.length > 0) {
|
||||||
const prices = await getPricesUncached(uncached);
|
const batches: Promise<MarketTypePrice[]>[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < uncached.length; i += BATCH_SIZE) {
|
||||||
|
batches.push(getPricesUncached(uncached.slice(i, i + BATCH_SIZE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const prices = (await Promise.all(batches)).flat();
|
||||||
|
|
||||||
prices.forEach(p => cache.set(rId, p.type.id, p));
|
prices.forEach(p => cache.set(rId, p.type.id, p));
|
||||||
return [ ...cached, ...prices ];
|
return [ ...cached, ...prices ];
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { logResource } from '@/service';
|
import {logResource} from '@/service';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { MarketType } from "../type";
|
|
||||||
import { PriceGetter } from './MarketTypePrice';
|
|
||||||
|
|
||||||
export const evepraisalAxiosInstance = axios.create({
|
export const evepraisalAxiosInstance = axios.create({
|
||||||
baseURL: import.meta.env.VITE_EVEPRAISAL_URL,
|
baseURL: import.meta.env.VITE_EVEPRAISAL_URL,
|
||||||
@@ -11,21 +9,3 @@ export const evepraisalAxiosInstance = axios.create({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
logResource(evepraisalAxiosInstance)
|
logResource(evepraisalAxiosInstance)
|
||||||
|
|
||||||
const batchSize = 100;
|
|
||||||
|
|
||||||
export const getEvepraisalPrices: PriceGetter = async types => {
|
|
||||||
const batches = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < types.length; i += batchSize) {
|
|
||||||
batches.push(evepraisalAxiosInstance.post(`/appraisal.json?market=jita&persist=no&raw_textarea=${types.slice(i, i + batchSize).map(t => t.name).join("%0A")}`));
|
|
||||||
}
|
|
||||||
return (await Promise.all(batches))
|
|
||||||
.flatMap(b => b.data.appraisal.items)
|
|
||||||
.map((item: any) => ({
|
|
||||||
type: types.find(t => t.name === item.typeName) as MarketType,
|
|
||||||
buy: item.prices.buy.max,
|
|
||||||
sell: item.prices.sell.min,
|
|
||||||
orderCount: item.prices.all.order_count
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { logResource } from '@/service';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { MarketType } from "../type";
|
|
||||||
import { PriceGetter } from './MarketTypePrice';
|
|
||||||
|
|
||||||
export const fuzzworkAxiosInstance = axios.create({
|
|
||||||
baseURL: import.meta.env.VITE_FUZZWORK_URL,
|
|
||||||
headers: {
|
|
||||||
'accept': 'application/json',
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
})
|
|
||||||
logResource(fuzzworkAxiosInstance)
|
|
||||||
|
|
||||||
const batchSize = 100;
|
|
||||||
|
|
||||||
export const getfuzzworkPrices: PriceGetter = async types => {
|
|
||||||
const batches = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < types.length; i += batchSize) {
|
|
||||||
batches.push(fuzzworkAxiosInstance.post(`/aggregates/?station=60003760&types=${types.slice(i, i + batchSize).map(t => t.id).join(",")}`));
|
|
||||||
}
|
|
||||||
return (await Promise.all(batches))
|
|
||||||
.flatMap(b => Object.entries(b.data))
|
|
||||||
.map(entry => {
|
|
||||||
const id = doParseInt(entry[0]);
|
|
||||||
const prices = entry[1] as any;
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: types.find(t => t.id === id) as MarketType,
|
|
||||||
buy: doParseFloat(prices?.buy?.max),
|
|
||||||
sell: doParseFloat(prices?.sell?.min),
|
|
||||||
orderCount: doParseInt(prices?.buy?.order_count) + doParseInt(prices?.sell?.order_count)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const doParseInt = (s?: string) => s ? parseInt(s) : 0;
|
|
||||||
const doParseFloat = (s?: string) => s ? parseFloat(s) : 0;
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import {marketApi} from '@/mammon/mammonService';
|
||||||
|
import {MarketTypePrice, PriceGetter} from './MarketTypePrice';
|
||||||
|
|
||||||
|
export const getMammonPrices: PriceGetter = async types => {
|
||||||
|
if (types.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const typesById = new Map(types.map(t => [t.id, t]));
|
||||||
|
const response = await marketApi.currentPrices(types.map(t => t.id));
|
||||||
|
|
||||||
|
return response.data.reduce<MarketTypePrice[]>((prices, p) => {
|
||||||
|
const type = typesById.get(p.marketTypeId);
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
prices.push({ type, buy: p.buy, sell: p.sell, orderCount: p.orderCount });
|
||||||
|
}
|
||||||
|
return prices;
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { esiAxiosInstance } from "@/service";
|
|
||||||
import { RegionalMarketCache } from '../RegionalMarketCache';
|
|
||||||
import { jitaId } from "../market";
|
|
||||||
|
|
||||||
|
|
||||||
export type EsiMarketOrderHistory = {
|
|
||||||
average: number;
|
|
||||||
date: string;
|
|
||||||
highest: number;
|
|
||||||
lowest: number;
|
|
||||||
order_count: number;
|
|
||||||
volume: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO use pinia store
|
|
||||||
const historyCache: RegionalMarketCache<EsiMarketOrderHistory[]> = new RegionalMarketCache(() => {
|
|
||||||
const date = new Date();
|
|
||||||
|
|
||||||
if (date.getUTCHours() >= 11) {
|
|
||||||
date.setUTCDate(date.getUTCDate() + 1);
|
|
||||||
}
|
|
||||||
date.setUTCHours(11, 0, 0, 0);
|
|
||||||
return date;
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getHistory = async (typeId: number, regionId?: number): Promise<EsiMarketOrderHistory[]> => {
|
|
||||||
const rId = regionId ?? jitaId;
|
|
||||||
|
|
||||||
return historyCache.computeIfAbsent(rId, typeId, async () => (await esiAxiosInstance.get(`/markets/${rId}/history/`, { params: { type_id: typeId } })).data);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EsiMarketOrderHistory } from "@/market";
|
import { MarketHistory } from "@/market";
|
||||||
|
|
||||||
export type HistoryQuartils = {
|
export type HistoryQuartils = {
|
||||||
totalVolume: number,
|
totalVolume: number,
|
||||||
@@ -7,7 +7,7 @@ export type HistoryQuartils = {
|
|||||||
q3: number,
|
q3: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getHistoryQuartils = (history: EsiMarketOrderHistory[], days?: number): HistoryQuartils => {
|
export const getHistoryQuartils = (history: MarketHistory[], days?: number): HistoryQuartils => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
const volumes = history
|
const volumes = history
|
||||||
@@ -51,7 +51,7 @@ export const getHistoryQuartils = (history: EsiMarketOrderHistory[], days?: numb
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const estimateVolume = (history: EsiMarketOrderHistory): number => {
|
const estimateVolume = (history: MarketHistory): number => {
|
||||||
if (history.volume === 0) {
|
if (history.volume === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { MarketHistoryResponse } from "@/generated/mammon";
|
||||||
|
import { marketApi } from "@/mammon";
|
||||||
|
|
||||||
|
export type MarketHistory = MarketHistoryResponse;
|
||||||
|
|
||||||
|
export const getHistory = async (typeId: number): Promise<MarketHistory[]> =>
|
||||||
|
(await marketApi.findHistory(typeId)).data;
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './EsiMarketOrderHistory';
|
export * from './MarketHistory';
|
||||||
export * from './HistoryQuartils';
|
export * from './HistoryQuartils';
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {SliderCheckbox} from '@/components';
|
import {SliderCheckbox} from '@/components';
|
||||||
import {SortableHeader, useSort, VirtualScrollTable} from '@/components/table';
|
import {SortableHeader, useSort, VirtualScrollTable} from '@/components/table';
|
||||||
import {getHistoryQuartils, MarketType, MarketTypeLabel, TaxInput, useMarketTaxStore} from "@/market";
|
import {formatIsk, percentFormater} from "@/formaters";
|
||||||
import {BookmarkSlashIcon, ShoppingCartIcon} from '@heroicons/vue/24/outline';
|
import {MarketType, MarketTypeLabel} from "@/market";
|
||||||
|
import {ShoppingCartIcon} from '@heroicons/vue/24/outline';
|
||||||
import {useStorage} from '@vueuse/core';
|
import {useStorage} from '@vueuse/core';
|
||||||
import {computed, ref} from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
import {useAcquiredTypesStore} from '../acquisition';
|
import {useAcquiredTypesStore} from '../acquisition';
|
||||||
import {TrackingResult} from './tracking';
|
import {ScanResult} from './scan';
|
||||||
|
|
||||||
type Result = {
|
type Result = {
|
||||||
type: MarketType;
|
type: MarketType;
|
||||||
@@ -17,25 +18,28 @@ type Result = {
|
|||||||
q1: number;
|
q1: number;
|
||||||
median: number;
|
median: number;
|
||||||
q3: number;
|
q3: number;
|
||||||
|
totalVolume: number;
|
||||||
acquisitions: number;
|
acquisitions: number;
|
||||||
profit: number;
|
profit: number;
|
||||||
score: number;
|
score: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
items?: TrackingResult[];
|
items?: ScanResult[];
|
||||||
infoOnly?: boolean;
|
infoOnly?: boolean;
|
||||||
ignoredColums?: string[] | string;
|
ignoredColums?: string[] | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'buy', type: MarketType, buy: number, sell: number): void;
|
(e: 'buy', type: MarketType, buy: number, sell: number): void;
|
||||||
(e: 'remove', type: MarketType): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const scoreFormater = new Intl.NumberFormat("en-US", {
|
const scoreFormater = new Intl.NumberFormat("en-US", {
|
||||||
maximumFractionDigits: 0
|
maximumFractionDigits: 0
|
||||||
});
|
});
|
||||||
|
const volumeFormater = new Intl.NumberFormat("en-US", {
|
||||||
|
maximumFractionDigits: 0
|
||||||
|
});
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
items: () => [],
|
items: () => [],
|
||||||
@@ -44,11 +48,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
});
|
});
|
||||||
defineEmits<Emits>();
|
defineEmits<Emits>();
|
||||||
|
|
||||||
const marketTaxStore = useMarketTaxStore();
|
|
||||||
const acquiredTypesStore = useAcquiredTypesStore();
|
const acquiredTypesStore = useAcquiredTypesStore();
|
||||||
|
|
||||||
const days = useStorage('market-tracking-days', 365);
|
const threshold = useStorage('market-scan-threshold', 10);
|
||||||
const threshold = useStorage('market-tracking-threshold', 10);
|
|
||||||
const filter = ref("");
|
const filter = ref("");
|
||||||
const onlyCheap = ref(false);
|
const onlyCheap = ref(false);
|
||||||
const columnsToIgnore = computed(() => {
|
const columnsToIgnore = computed(() => {
|
||||||
@@ -62,9 +64,6 @@ const columnsToIgnore = computed(() => {
|
|||||||
const { sortedArray, headerProps, showColumn } = useSort<Result>(computed(() => props.items
|
const { sortedArray, headerProps, showColumn } = useSort<Result>(computed(() => props.items
|
||||||
.filter(r => r.type.name.toLowerCase().includes(filter.value.toLowerCase()))
|
.filter(r => r.type.name.toLowerCase().includes(filter.value.toLowerCase()))
|
||||||
.map(r => {
|
.map(r => {
|
||||||
const quartils = getHistoryQuartils(r.history, days.value);
|
|
||||||
const profit = quartils.q1 === 0 || quartils.q3 === 0 ? 0 : marketTaxStore.calculateProfit(quartils.q1, quartils.q3);
|
|
||||||
const score = profit <= 0 ? 0 : Math.sqrt((Math.pow(quartils.totalVolume, 1.1) * Math.pow(quartils.q1, 1.2) * Math.pow(profit, 0.5) * Math.pow(Math.max(1, r.orderCount), -0.7)) / days.value);
|
|
||||||
const acquisitions = columnsToIgnore.value.includes('acquisitions') ? 0 : acquiredTypesStore.acquiredTypes
|
const acquisitions = columnsToIgnore.value.includes('acquisitions') ? 0 : acquiredTypesStore.acquiredTypes
|
||||||
.filter(t => t.type === r.type.id)
|
.filter(t => t.type === r.type.id)
|
||||||
.reduce((a, b) => a + b.remaining, 0);
|
.reduce((a, b) => a + b.remaining, 0);
|
||||||
@@ -75,12 +74,13 @@ const { sortedArray, headerProps, showColumn } = useSort<Result>(computed(() =>
|
|||||||
name: r.type.name,
|
name: r.type.name,
|
||||||
buy: r.buy,
|
buy: r.buy,
|
||||||
sell: r.sell,
|
sell: r.sell,
|
||||||
q1: quartils.q1,
|
q1: r.q1,
|
||||||
median: quartils.median,
|
median: r.median,
|
||||||
q3: quartils.q3,
|
q3: r.q3,
|
||||||
|
totalVolume: r.totalVolume,
|
||||||
acquisitions,
|
acquisitions,
|
||||||
profit,
|
profit: r.profit,
|
||||||
score
|
score: r.score
|
||||||
};
|
};
|
||||||
}).filter(r => !onlyCheap.value || (r.buy <= r.q1 && r.profit >= (threshold.value / 100)))), {
|
}).filter(r => !onlyCheap.value || (r.buy <= r.q1 && r.profit >= (threshold.value / 100)))), {
|
||||||
defaultSortKey: 'score',
|
defaultSortKey: 'score',
|
||||||
@@ -104,15 +104,10 @@ const getLineColor = (result: Result) => {
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="!infoOnly" class="flex mb-2 mt-4">
|
<div v-if="!infoOnly" class="flex mb-2 mt-4">
|
||||||
<div class="flex justify-self-end ms-auto">
|
<div class="flex justify-self-end ms-auto">
|
||||||
<TaxInput />
|
|
||||||
<div class="end">
|
<div class="end">
|
||||||
<span>Profit Threshold: </span>
|
<span>Profit Threshold: </span>
|
||||||
<input type="number" min="0" max="1000" step="1" v-model="threshold" />
|
<input type="number" min="0" max="1000" step="1" v-model="threshold" />
|
||||||
</div>
|
</div>
|
||||||
<div class="end">
|
|
||||||
<span>Days: </span>
|
|
||||||
<input type="number" min="1" max="365" step="1" v-model="days" />
|
|
||||||
</div>
|
|
||||||
<div class="end flex">
|
<div class="end flex">
|
||||||
<SliderCheckbox class="me-1" v-model="onlyCheap" /> Show only cheap items
|
<SliderCheckbox class="me-1" v-model="onlyCheap" /> Show only cheap items
|
||||||
</div>
|
</div>
|
||||||
@@ -132,6 +127,7 @@ const getLineColor = (result: Result) => {
|
|||||||
<SortableHeader v-bind="headerProps" sortKey="q1">Q1</SortableHeader>
|
<SortableHeader v-bind="headerProps" sortKey="q1">Q1</SortableHeader>
|
||||||
<SortableHeader v-bind="headerProps" sortKey="median">Median</SortableHeader>
|
<SortableHeader v-bind="headerProps" sortKey="median">Median</SortableHeader>
|
||||||
<SortableHeader v-bind="headerProps" sortKey="q3">Q3</SortableHeader>
|
<SortableHeader v-bind="headerProps" sortKey="q3">Q3</SortableHeader>
|
||||||
|
<SortableHeader v-bind="headerProps" sortKey="totalVolume">Volume</SortableHeader>
|
||||||
<SortableHeader v-bind="headerProps" sortKey="profit">Profit</SortableHeader>
|
<SortableHeader v-bind="headerProps" sortKey="profit">Profit</SortableHeader>
|
||||||
<SortableHeader v-bind="headerProps" sortKey="score">Score</SortableHeader>
|
<SortableHeader v-bind="headerProps" sortKey="score">Score</SortableHeader>
|
||||||
<SortableHeader v-bind="headerProps" sortKey="acquisitions">Acquisitions</SortableHeader>
|
<SortableHeader v-bind="headerProps" sortKey="acquisitions">Acquisitions</SortableHeader>
|
||||||
@@ -148,12 +144,12 @@ const getLineColor = (result: Result) => {
|
|||||||
<td v-if="showColumn('q1')" class="text-right">{{ formatIsk(r.data.q1) }}</td>
|
<td v-if="showColumn('q1')" class="text-right">{{ formatIsk(r.data.q1) }}</td>
|
||||||
<td v-if="showColumn('median')" class="text-right">{{ formatIsk(r.data.median) }}</td>
|
<td v-if="showColumn('median')" class="text-right">{{ formatIsk(r.data.median) }}</td>
|
||||||
<td v-if="showColumn('q3')" class="text-right">{{ formatIsk(r.data.q3) }}</td>
|
<td v-if="showColumn('q3')" class="text-right">{{ formatIsk(r.data.q3) }}</td>
|
||||||
|
<td v-if="showColumn('totalVolume')" class="text-right">{{ volumeFormater.format(r.data.totalVolume) }}</td>
|
||||||
<td v-if="showColumn('profit')" class="text-right">{{ percentFormater.format(r.data.profit) }}</td>
|
<td v-if="showColumn('profit')" class="text-right">{{ percentFormater.format(r.data.profit) }}</td>
|
||||||
<td v-if="showColumn('score')" class="text-right">{{ scoreFormater.format(r.data.score) }}</td>
|
<td v-if="showColumn('score')" class="text-right">{{ scoreFormater.format(r.data.score) }}</td>
|
||||||
<td v-if="showColumn('acquisitions')" class="text-right">{{ r.data.acquisitions }}</td>
|
<td v-if="showColumn('acquisitions')" class="text-right">{{ r.data.acquisitions }}</td>
|
||||||
<td v-if="showColumn('buttons')" class="text-right">
|
<td v-if="showColumn('buttons')" class="text-right">
|
||||||
<button class="btn-icon me-1" title="Add acquisitions" @click="$emit('buy', r.data.type, r.data.buy, r.data.sell)"><ShoppingCartIcon /></button>
|
<button class="btn-icon me-1" title="Add acquisitions" @click="$emit('buy', r.data.type, r.data.buy, r.data.sell)"><ShoppingCartIcon /></button>
|
||||||
<button class="btn-icon me-1" title="Untrack" @click="$emit('remove', r.data.type)"><BookmarkSlashIcon /></button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -171,4 +167,4 @@ const getLineColor = (result: Result) => {
|
|||||||
div.end {
|
div.end {
|
||||||
@apply justify-self-end ms-2;
|
@apply justify-self-end ms-2;
|
||||||
}
|
}
|
||||||
</style>../history/HistoryQuartils
|
</style>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './scan';
|
||||||
|
|
||||||
|
export { default as ScanResultTable } from './ScanResultTable.vue';
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { MarketType } from "@/market";
|
||||||
|
import { MarketScanResponse } from "@/generated/mammon";
|
||||||
|
|
||||||
|
export type ScanResult = {
|
||||||
|
type: MarketType;
|
||||||
|
buy: number;
|
||||||
|
sell: number;
|
||||||
|
q1: number;
|
||||||
|
median: number;
|
||||||
|
q3: number;
|
||||||
|
totalVolume: number;
|
||||||
|
profit: number;
|
||||||
|
score: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toScanResult = (res: MarketScanResponse, type: MarketType): ScanResult => ({
|
||||||
|
type,
|
||||||
|
buy: res.buy,
|
||||||
|
sell: res.sell,
|
||||||
|
q1: res.q1,
|
||||||
|
median: res.median,
|
||||||
|
q3: res.q3,
|
||||||
|
totalVolume: res.totalVolume,
|
||||||
|
profit: res.profit,
|
||||||
|
score: res.score,
|
||||||
|
});
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {useMarketTaxStore} from "./tax";
|
import {useMarketTaxStore} from "./tax";
|
||||||
|
|
||||||
const { brokerFee, scc } = storeToRefs(useMarketTaxStore());
|
const marketTaxStore = useMarketTaxStore();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="end">
|
<div class="end">
|
||||||
<span>Broker Fee: </span>
|
<span>Broker Fee: </span>
|
||||||
<input type="number" min="1" max="3" step="0.01" v-model="brokerFee" />
|
<input type="number" min="1" max="3" step="0.01" v-model="marketTaxStore.brokerFee" />
|
||||||
</div>
|
</div>
|
||||||
<div class="end">
|
<div class="end">
|
||||||
<span>SCC: </span>
|
<span>SCC: </span>
|
||||||
<input type="number" min="3.6" max="8" step="0.01" v-model="scc" >
|
<input type="number" min="3.6" max="8" step="0.01" v-model="marketTaxStore.scc" >
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
export * from './tracking';
|
|
||||||
|
|
||||||
export { default as TrackingResultTable } from './TrackingResultTable.vue';
|
|
||||||
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { EsiMarketOrderHistory, getHistory, MarketType, MarketTypePrice } from "@/market";
|
|
||||||
import log from "loglevel";
|
|
||||||
import { defineStore } from "pinia";
|
|
||||||
import { computed, ref } from "vue";
|
|
||||||
|
|
||||||
export type TrackingResult = {
|
|
||||||
type: MarketType;
|
|
||||||
history: EsiMarketOrderHistory[];
|
|
||||||
buy: number,
|
|
||||||
sell: number,
|
|
||||||
orderCount: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
const endpoint = '/api/types_tracking/';
|
|
||||||
|
|
||||||
export const useMarketTrackingStore = defineStore('marketTracking', () => {
|
|
||||||
const trackedTypes = ref<any[]>([]); // TODO
|
|
||||||
|
|
||||||
const types = computed(() => trackedTypes.value.map(item => item.type) ?? []);
|
|
||||||
const addType = async (type: number) => {
|
|
||||||
const found = trackedTypes.value.find(item => item.type === type);
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
log.info(`Tracking type ${type}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const removeType = async (type: number) => {
|
|
||||||
const found = trackedTypes.value.find(item => item.type === type);
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
trackedTypes.value = trackedTypes.value.filter(t => t.id !== found.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { types, addType, removeType };
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createResult = async (id: number, price: MarketTypePrice): Promise<TrackingResult> => ({ history: await getHistory(id), ...price });
|
|
||||||
@@ -1,30 +1,23 @@
|
|||||||
import {esiAxiosInstance} from '@/service';
|
import {marketApi} from '@/mammon/mammonService';
|
||||||
|
import type {MarketTypeResponse} from '@/generated/mammon';
|
||||||
|
|
||||||
export type MarketType = {
|
export type MarketType = MarketTypeResponse;
|
||||||
id: number;
|
|
||||||
group_id: number;
|
|
||||||
market_group_id: number;
|
|
||||||
name: string;
|
|
||||||
published: boolean;
|
|
||||||
description: string;
|
|
||||||
base_price: number;
|
|
||||||
icon_id: number;
|
|
||||||
volume: number;
|
|
||||||
portion_size: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cache = new Map<number, MarketType>(); // TODO move to pinia store
|
const cache = new Map<number, MarketType>(); // TODO move to pinia store
|
||||||
|
|
||||||
const fetchType = (id: number): Promise<MarketType> => {
|
const BATCH_SIZE = 100;
|
||||||
if (cache.has(id)) {
|
|
||||||
return Promise.resolve(cache.get(id)!);
|
const fetchTypes = async (ids: number[]): Promise<void> => {
|
||||||
|
const missing = ids.filter(id => !cache.has(id));
|
||||||
|
if (missing.length === 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return esiAxiosInstance.get<Omit<MarketType, 'id'> & { type_id: number }>(`/universe/types/${id}/`).then(r => {
|
const batches: Promise<MarketType[]>[] = [];
|
||||||
const { type_id, ...rest } = r.data;
|
for (let i = 0; i < missing.length; i += BATCH_SIZE) {
|
||||||
const marketType: MarketType = { id: type_id, ...rest };
|
batches.push(marketApi.findTypes(missing.slice(i, i + BATCH_SIZE)).then(r => r.data));
|
||||||
cache.set(id, marketType);
|
}
|
||||||
return marketType;
|
const results = await Promise.all(batches);
|
||||||
});
|
results.flat().forEach(t => cache.set(t.id, t));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMarketType = async (type: string | number): Promise<MarketType> => (await getMarketTypes([type]))[0];
|
export const getMarketType = async (type: string | number): Promise<MarketType> => (await getMarketTypes([type]))[0];
|
||||||
@@ -33,28 +26,15 @@ export const getMarketTypes = async (types: (string | number)[]): Promise<Market
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const ids = types.filter((t): t is number => typeof t === 'number');
|
const ids = types.filter((t): t is number => typeof t === 'number');
|
||||||
return Promise.all(ids.map(fetchType));
|
await fetchTypes(ids);
|
||||||
|
return ids.map(id => cache.get(id)).filter((t): t is MarketType => t !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
const blueprintMarketGroups = [ // TODO add all groups
|
|
||||||
2,
|
|
||||||
2157,
|
|
||||||
2159,
|
|
||||||
2339,
|
|
||||||
2160,
|
|
||||||
211,
|
|
||||||
1016,
|
|
||||||
339,
|
|
||||||
2290,
|
|
||||||
357,
|
|
||||||
1530,
|
|
||||||
359,
|
|
||||||
1531,
|
|
||||||
1532,
|
|
||||||
1533,
|
|
||||||
358
|
|
||||||
]
|
|
||||||
|
|
||||||
export const searchMarketTypes = async (search: string): Promise<MarketType[]> => {
|
export const searchMarketTypes = async (search: string): Promise<MarketType[]> => {
|
||||||
return []
|
if (search.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const types = await marketApi.searchTypes(search).then(r => r.data);
|
||||||
|
types.forEach(t => cache.set(t.id, t));
|
||||||
|
return types;
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ watchEffect(async () => {
|
|||||||
<template>
|
<template>
|
||||||
<div @click="() => isOpen = true" v-on-click-outside="() => isOpen = false">
|
<div @click="() => isOpen = true" v-on-click-outside="() => isOpen = false">
|
||||||
<div class="fake-input">
|
<div class="fake-input">
|
||||||
<img v-if="modelValue?.type_id" :src="`https://images.evetech.net/types/${modelValue.type_id}/icon?size=32`" alt="" />
|
<img v-if="modelValue?.id" :src="`https://images.evetech.net/types/${modelValue.id}/icon?size=32`" alt="" />
|
||||||
<input type="text" v-model="name" @keyup.enter="submit" @keyup.down="moveDown" @keyup.up="moveUp" />
|
<input type="text" v-model="name" @keyup.enter="submit" @keyup.down="moveDown" @keyup.up="moveUp" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="suggestions.length > 1" class="z-20 absolute w-96">
|
<div v-if="suggestions.length > 1" class="z-20 absolute w-96">
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {mammonAddCharacterUrl} from "@/mammon";
|
import {mammonAddCharacterUrl} from "@/mammon";
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {CharacterLabel, useCharactersStore} from "@/characters";
|
import {CharacterLabel, useCharactersStore} from "@/characters";
|
||||||
import {ArrowPathIcon} from '@heroicons/vue/24/outline';
|
import {ArrowPathIcon} from '@heroicons/vue/24/outline';
|
||||||
|
|
||||||
const charactersStore = useCharactersStore()
|
const charactersStore = useCharactersStore()
|
||||||
const {characters} = storeToRefs(charactersStore);
|
|
||||||
const {reloadActivities} = charactersStore;
|
|
||||||
|
|
||||||
const addCharacter = () => {
|
const addCharacter = () => {
|
||||||
window.location.replace(mammonAddCharacterUrl);
|
window.location.replace(mammonAddCharacterUrl);
|
||||||
@@ -19,9 +16,9 @@ const addCharacter = () => {
|
|||||||
<div class="mb-4 border-b-1 flex justify-end">
|
<div class="mb-4 border-b-1 flex justify-end">
|
||||||
<button class="mb-2" @click="addCharacter">Add chacarcter</button>
|
<button class="mb-2" @click="addCharacter">Add chacarcter</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="character in characters" :key="character.characterId" class="flex items-center mb-2">
|
<div v-for="character in charactersStore.characters" :key="character.characterId" class="flex items-center mb-2">
|
||||||
<CharacterLabel class="grow" :character="character" />
|
<CharacterLabel class="grow" :character="character" />
|
||||||
<button class="btn-icon" @click="reloadActivities(character.characterId)"><ArrowPathIcon /></button>
|
<button class="btn-icon" @click="charactersStore.reloadActivities(character.characterId)"><ArrowPathIcon /></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -4,14 +4,14 @@ import {EditLedgerModal, useLedgersStore} from "@/ledger";
|
|||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {activityApi, processingApi} from "@/mammon";
|
import {activityApi, processingApi} from "@/mammon";
|
||||||
|
|
||||||
const {refresh} = useLedgersStore();
|
const ledgersStore = useLedgersStore();
|
||||||
|
|
||||||
const editLedgerModal = ref<typeof EditLedgerModal>();
|
const editLedgerModal = ref<typeof EditLedgerModal>();
|
||||||
|
|
||||||
const processActivities = async () => {
|
const processActivities = async () => {
|
||||||
await activityApi.fetchAllNewActivities();
|
await activityApi.fetchAllNewActivities();
|
||||||
await processingApi.processNewActivities();
|
await processingApi.processNewActivities();
|
||||||
await refresh();
|
await ledgersStore.refresh();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import {routeNames} from '@/routes';
|
|||||||
<RouterLink :to="{name: routeNames.marketTypes}" class="tab">
|
<RouterLink :to="{name: routeNames.marketTypes}" class="tab">
|
||||||
<span>Item Info</span>
|
<span>Item Info</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink to="/market/tracking" class="tab">
|
<RouterLink to="/market/scan" class="tab">
|
||||||
<span>Tracking</span>
|
<span>Scan</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink to="/market/acquisitions" class="tab">
|
<RouterLink to="/market/acquisitions" class="tab">
|
||||||
<span>Acquisitions</span>
|
<span>Acquisitions</span>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {CharacterLabel, useCharactersStore} from "@/characters";
|
|||||||
import {formatEveDate} from "@/formaters.ts";
|
import {formatEveDate} from "@/formaters.ts";
|
||||||
|
|
||||||
const {ledgerId} = useLedgerParam();
|
const {ledgerId} = useLedgerParam();
|
||||||
const {findById} = useCharactersStore();
|
const charactersStore = useCharactersStore();
|
||||||
|
|
||||||
const transactions = computedAsync<TransactionResponse[]>(async () => {
|
const transactions = computedAsync<TransactionResponse[]>(async () => {
|
||||||
if (ledgerId.value) {
|
if (ledgerId.value) {
|
||||||
@@ -21,7 +21,7 @@ const transactions = computedAsync<TransactionResponse[]>(async () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { sortedArray, headerProps } = useSort(computedAsync(() => Promise.all(transactions.value.map(async transaction => {
|
const { sortedArray, headerProps } = useSort(computedAsync(() => Promise.all(transactions.value.map(async transaction => {
|
||||||
const character = await findById(transaction.characterId);
|
const character = await charactersStore.findById(transaction.characterId);
|
||||||
return {
|
return {
|
||||||
character,
|
character,
|
||||||
characterName: character?.name ?? "",
|
characterName: character?.name ?? "",
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {EditLedgerModal, Ledger, LedgerLabel, useLedgersStore} from "@/ledger";
|
import {EditLedgerModal, Ledger, LedgerLabel, useLedgersStore} from "@/ledger";
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {nextTick, ref} from "vue";
|
import {nextTick, ref} from "vue";
|
||||||
import {PencilSquareIcon} from "@heroicons/vue/24/outline";
|
import {PencilSquareIcon} from "@heroicons/vue/24/outline";
|
||||||
import {IskLabel} from "@/market";
|
import {IskLabel} from "@/market";
|
||||||
import {SortableHeader, useSort, VirtualScrollTable} from "@/components/table";
|
import {SortableHeader, useSort, VirtualScrollTable} from "@/components/table";
|
||||||
|
|
||||||
const {ledgers} = storeToRefs(useLedgersStore());
|
const ledgersStore = useLedgersStore();
|
||||||
|
|
||||||
const { sortedArray, headerProps } = useSort<Ledger>(ledgers);
|
const { sortedArray, headerProps } = useSort<Ledger>(() => ledgersStore.ledgers);
|
||||||
|
|
||||||
const editModal = ref<typeof EditLedgerModal>();
|
const editModal = ref<typeof EditLedgerModal>();
|
||||||
const editingLedgerId = ref("");
|
const editingLedgerId = ref("");
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ watch(() => acquiredTypesStore.acquiredTypes, async itms => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prices = await apraisalStore.getPrices(await getMarketTypes(itms.map(i => i.type)));
|
const prices = await apraisalStore.getPrices(await getMarketTypes([...new Set(itms.map(i => i.type))]));
|
||||||
|
|
||||||
items.value = itms.map(i => {
|
items.value = itms.map(i => {
|
||||||
const price = prices.find(p => p.type.id === i.type) as MarketTypePrice;
|
const price = prices.find(p => p.type.id === i.type) as MarketTypePrice;
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {getMarketTypes, TaxInput, useMarketTaxStore} from "@/market";
|
||||||
|
import {BuyModal} from '@/market/acquisition';
|
||||||
|
import {ScanResult, ScanResultTable, toScanResult} from '@/market/scan';
|
||||||
|
import {marketApi} from "@/mammon";
|
||||||
|
import {useStorage} from "@vueuse/core";
|
||||||
|
import {ref, watch} from 'vue';
|
||||||
|
|
||||||
|
const buyModal = ref<typeof BuyModal>();
|
||||||
|
|
||||||
|
const marketTaxStore = useMarketTaxStore();
|
||||||
|
const days = useStorage('market-scan-days', 365);
|
||||||
|
const items = ref<ScanResult[]>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const scan = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const { data } = await marketApi.scanMarket(
|
||||||
|
days.value,
|
||||||
|
marketTaxStore.brokerFee / 100,
|
||||||
|
marketTaxStore.scc / 100
|
||||||
|
);
|
||||||
|
const types = await getMarketTypes(data.map(r => r.marketTypeId));
|
||||||
|
|
||||||
|
items.value = data.flatMap(r => {
|
||||||
|
const type = types.find(t => t.id === r.marketTypeId);
|
||||||
|
|
||||||
|
return type ? [toScanResult(r, type)] : [];
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([days, () => marketTaxStore.brokerFee, () => marketTaxStore.scc], scan, { immediate: true });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex mb-2 mt-4">
|
||||||
|
<div class="flex justify-self-end ms-auto">
|
||||||
|
<TaxInput />
|
||||||
|
<div class="end">
|
||||||
|
<span>Days: </span>
|
||||||
|
<input type="number" min="1" max="365" step="1" v-model="days" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div v-if="loading" class="text-center mt-4">
|
||||||
|
<span>Scanning market…</span>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<ScanResultTable :items="items" @buy="(type, buy, sell) => buyModal?.open(type, { 'Buy': buy, 'Sell': sell })" />
|
||||||
|
<BuyModal ref="buyModal" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@reference "@/style.css";
|
||||||
|
div.end {
|
||||||
|
@apply justify-self-end ms-2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { Modal, ProgressBar } from "@/components";
|
|
||||||
import { MarketType, MarketTypeInput, MarketTypePrice, getHistory, getMarketTypes, useApraisalStore } from "@/market";
|
|
||||||
import { BuyModal } from '@/market/acquisition';
|
|
||||||
import { TrackingResult, TrackingResultTable, createResult, useMarketTrackingStore } from '@/market/tracking';
|
|
||||||
import { ref, watch } from 'vue';
|
|
||||||
|
|
||||||
|
|
||||||
const buyModal = ref<typeof BuyModal>();
|
|
||||||
|
|
||||||
const item = ref<MarketType>();
|
|
||||||
|
|
||||||
const apraisalStore = useApraisalStore();
|
|
||||||
const marketTrackingStore = useMarketTrackingStore();
|
|
||||||
const items = ref<TrackingResult[]>([]);
|
|
||||||
const addOrRelaod = async (type: MarketType) => {
|
|
||||||
const typeID = type.id;
|
|
||||||
const [history, price] = await Promise.all([
|
|
||||||
getHistory(typeID),
|
|
||||||
apraisalStore.getPrice(type)
|
|
||||||
]);
|
|
||||||
const itm = {
|
|
||||||
type,
|
|
||||||
history,
|
|
||||||
buy: price.buy,
|
|
||||||
sell: price.sell,
|
|
||||||
orderCount: price.orderCount
|
|
||||||
};
|
|
||||||
|
|
||||||
if (items.value.some(i => i.type.id === typeID)) {
|
|
||||||
items.value = items.value.map(i => i.type.id === typeID ? itm : i);
|
|
||||||
} else {
|
|
||||||
items.value = [ ...items.value, itm];
|
|
||||||
marketTrackingStore.addType(typeID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const addItem = async () => {
|
|
||||||
if (!item.value) {
|
|
||||||
// TODO error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addOrRelaod(item.value);
|
|
||||||
item.value = undefined;
|
|
||||||
}
|
|
||||||
const removeItem = (type: MarketType) => {
|
|
||||||
items.value = items.value.filter(i => i.type.id !== type.id);
|
|
||||||
marketTrackingStore.removeType(type.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => marketTrackingStore.types, async t => {
|
|
||||||
const typesToLoad = t.filter(t => !items.value.some(i => i.type.id === t));
|
|
||||||
|
|
||||||
if (typesToLoad.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prices = await apraisalStore.getPrices(await getMarketTypes(typesToLoad));
|
|
||||||
|
|
||||||
typesToLoad.forEach(async i => items.value.push(await createResult(i, prices.find(p => p.type.id === i) as MarketTypePrice)));
|
|
||||||
}, { immediate: true });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="grid mb-2 mt-4">
|
|
||||||
<div class="w-auto flex">
|
|
||||||
<span>Item: </span>
|
|
||||||
<MarketTypeInput class="ms-2" v-model="item" @submit="addItem"/>
|
|
||||||
<button class="justify-self-end ms-2" @click="addItem">Add</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<template v-if="items.length > 0">
|
|
||||||
<hr />
|
|
||||||
<TrackingResultTable :items="items" @buy="(type, buy, sell) => buyModal?.open(type, { 'Buy': buy, 'Sell': sell })" @remove="removeItem" />
|
|
||||||
<BuyModal ref="buyModal" />
|
|
||||||
<Modal :open="items.length > 0 && items.length < marketTrackingStore.types.length">
|
|
||||||
<div class="ms-auto me-auto mb-2 w-96">
|
|
||||||
<ProgressBar :value="items.length" :total="marketTrackingStore.types.length" />
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ClipboardButton} from '@/components';
|
import {ClipboardButton} from '@/components';
|
||||||
import {getMarketType, MarketType, MarketTypeInput, useApraisalStore} from "@/market";
|
import {getMarketType, MarketType, MarketTypeInput, useApraisalStore, useMarketTaxStore} from "@/market";
|
||||||
import {AcquisitionResultTable, BuyModal, useAcquiredTypesStore} from '@/market/acquisition';
|
import {AcquisitionResultTable, BuyModal, useAcquiredTypesStore} from '@/market/acquisition';
|
||||||
import {createResult, TrackingResultTable, useMarketTrackingStore} from '@/market/tracking';
|
import {ScanResultTable, toScanResult} from '@/market/scan';
|
||||||
import {BookmarkIcon, BookmarkSlashIcon, ShoppingCartIcon} from '@heroicons/vue/24/outline';
|
import {marketApi} from "@/mammon";
|
||||||
|
import {ShoppingCartIcon} from '@heroicons/vue/24/outline';
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import {computed, ref, watch} from "vue";
|
import {computed, ref, watch} from "vue";
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import {routeNames} from "@/routes";
|
import {routeNames} from "@/routes";
|
||||||
import {computedAsync} from "@vueuse/core";
|
import {computedAsync, useStorage} from "@vueuse/core";
|
||||||
|
|
||||||
const buyModal = ref<typeof BuyModal>();
|
const buyModal = ref<typeof BuyModal>();
|
||||||
|
|
||||||
@@ -18,12 +19,25 @@ const item = ref<MarketType>();
|
|||||||
const inputItem = ref<MarketType>();
|
const inputItem = ref<MarketType>();
|
||||||
|
|
||||||
const apraisalStore = useApraisalStore();
|
const apraisalStore = useApraisalStore();
|
||||||
|
const marketTaxStore = useMarketTaxStore();
|
||||||
|
const days = useStorage('market-scan-days', 365);
|
||||||
const price = computedAsync(() => item.value ? apraisalStore.getPrice(item.value) : undefined);
|
const price = computedAsync(() => item.value ? apraisalStore.getPrice(item.value) : undefined);
|
||||||
const marketTrackingStore = useMarketTrackingStore();
|
const result = computedAsync(async () => {
|
||||||
const result = computedAsync(async () => item.value && price.value ? await createResult(item.value?.id, price.value) : undefined);
|
if (!item.value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await marketApi.scanMarketType(
|
||||||
|
item.value.id,
|
||||||
|
days.value,
|
||||||
|
marketTaxStore.brokerFee / 100,
|
||||||
|
marketTaxStore.scc / 100
|
||||||
|
);
|
||||||
|
|
||||||
|
return toScanResult(data, item.value);
|
||||||
|
});
|
||||||
const acquiredTypesStore = useAcquiredTypesStore();
|
const acquiredTypesStore = useAcquiredTypesStore();
|
||||||
|
|
||||||
const isTracked = computed(() => item.value ? marketTrackingStore.types.includes(item.value.id) : false);
|
|
||||||
const acquisitions = computed(() => {
|
const acquisitions = computed(() => {
|
||||||
const p = price.value;
|
const p = price.value;
|
||||||
|
|
||||||
@@ -36,17 +50,6 @@ const acquisitions = computed(() => {
|
|||||||
sell: p.sell
|
sell: p.sell
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
const toogleTracking = () => {
|
|
||||||
if (!item.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isTracked.value) {
|
|
||||||
marketTrackingStore.removeType(item.value.id);
|
|
||||||
} else {
|
|
||||||
marketTrackingStore.addType(item.value.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const view = () => {
|
const view = () => {
|
||||||
if (!inputItem.value) {
|
if (!inputItem.value) {
|
||||||
return;
|
return;
|
||||||
@@ -93,10 +96,6 @@ watch(useRoute(), async route => {
|
|||||||
<div class="ms-auto">
|
<div class="ms-auto">
|
||||||
<ClipboardButton class="ms-1" :value="item.name" />
|
<ClipboardButton class="ms-1" :value="item.name" />
|
||||||
<button v-if="price" class="btn-icon ms-1" title="Add acquisitions" @click="buyModal?.open(item, { 'Buy': price.buy, 'Sell': price.sell })"><ShoppingCartIcon /></button>
|
<button v-if="price" class="btn-icon ms-1" title="Add acquisitions" @click="buyModal?.open(item, { 'Buy': price.buy, 'Sell': price.sell })"><ShoppingCartIcon /></button>
|
||||||
<button class="btn-icon ms-1" :title="isTracked ? 'Untrack' : 'Track'" @click="toogleTracking">
|
|
||||||
<BookmarkSlashIcon v-if="isTracked" />
|
|
||||||
<BookmarkIcon v-else />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="item.description" class="text-sm">{{ item.description }}</p>
|
<p v-if="item.description" class="text-sm">{{ item.description }}</p>
|
||||||
@@ -104,7 +103,7 @@ watch(useRoute(), async route => {
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="result" class="mb-4">
|
<div v-if="result" class="mb-4">
|
||||||
<span>Market Info:</span>
|
<span>Market Info:</span>
|
||||||
<TrackingResultTable :items="[result]" infoOnly :ignoredColums="['name', 'acquisitions']" />
|
<ScanResultTable :items="[result]" infoOnly :ignoredColums="['name', 'acquisitions']" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="acquisitions && acquisitions.length > 0">
|
<div v-if="acquisitions && acquisitions.length > 0">
|
||||||
<span>Acquisitions:</span>
|
<span>Acquisitions:</span>
|
||||||
|
|||||||
@@ -4,23 +4,20 @@ import {useRoute} from "vue-router";
|
|||||||
import {computed, ref, watch, watchEffect} from "vue";
|
import {computed, ref, watch, watchEffect} from "vue";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import {
|
import {
|
||||||
findCharacterRuleBookByCharacterId,
|
|
||||||
RuleBook,
|
RuleBook,
|
||||||
setCharacterRuleBookForCharacter,
|
useCharacterRuleBooksStore,
|
||||||
useRuleBooksStore
|
useRuleBooksStore
|
||||||
} from "@/rules";
|
} from "@/rules";
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {isMain, Ledger, LedgerSelect, systemLedger, useLedgersStore} from "@/ledger";
|
import {isMain, Ledger, LedgerSelect, systemLedger, useLedgersStore} from "@/ledger";
|
||||||
|
|
||||||
type Bindings = { [key: string]: Ledger; };
|
type Bindings = { [key: string]: Ledger; };
|
||||||
|
|
||||||
const ruleBookStore = useRuleBooksStore();
|
const ruleBookStore = useRuleBooksStore();
|
||||||
const {findById: findCharacterRuleBookById} = ruleBookStore;
|
const characterRuleBooksStore = useCharacterRuleBooksStore();
|
||||||
const {ruleBooks} = storeToRefs(ruleBookStore);
|
const charactersStore = useCharactersStore();
|
||||||
const {findById: findCharacterById} = useCharactersStore();
|
const ledgersStore = useLedgersStore();
|
||||||
const {ledgers} = storeToRefs(useLedgersStore());
|
|
||||||
|
|
||||||
const ledgersToUse = computed(() => [systemLedger, ...ledgers.value.filter(isMain)]);
|
const ledgersToUse = computed(() => [systemLedger, ...ledgersStore.ledgers.filter(isMain)]);
|
||||||
|
|
||||||
const character = ref<Character>();
|
const character = ref<Character>();
|
||||||
const ruleBook = ref<RuleBook>();
|
const ruleBook = ref<RuleBook>();
|
||||||
@@ -31,11 +28,11 @@ watchEffect(async () => {
|
|||||||
const characterId = character.value?.characterId;
|
const characterId = character.value?.characterId;
|
||||||
|
|
||||||
if (characterId) {
|
if (characterId) {
|
||||||
const characterRuleBook = await findCharacterRuleBookByCharacterId(characterId);
|
const characterRuleBook = characterRuleBooksStore.findByCharacterId(characterId);
|
||||||
|
|
||||||
ruleBook.value = findCharacterRuleBookById(characterRuleBook.ruleBookId);
|
ruleBook.value = ruleBookStore.findById(characterRuleBook?.ruleBook.ruleBookId ?? '');
|
||||||
bindings.value = Object.fromEntries(
|
bindings.value = Object.fromEntries(
|
||||||
Object.entries(characterRuleBook.bindings)
|
Object.entries(characterRuleBook?.bindings ?? {})
|
||||||
.map(([key, id]) => [key, ledgersToUse.value.find(l => l.ledgerId === id) ?? systemLedger])
|
.map(([key, id]) => [key, ledgersToUse.value.find(l => l.ledgerId === id) ?? systemLedger])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -46,7 +43,7 @@ const save = () => {
|
|||||||
const ruleBookId = ruleBook.value?.ruleBookId;
|
const ruleBookId = ruleBook.value?.ruleBookId;
|
||||||
|
|
||||||
if (characterId && ruleBookId) {
|
if (characterId && ruleBookId) {
|
||||||
setCharacterRuleBookForCharacter(characterId, {
|
characterRuleBooksStore.setForCharacter(characterId, {
|
||||||
ruleBookId,
|
ruleBookId,
|
||||||
bindings: Object.fromEntries(
|
bindings: Object.fromEntries(
|
||||||
Object.entries(bindings.value)
|
Object.entries(bindings.value)
|
||||||
@@ -60,7 +57,7 @@ watch(useRoute(), async route => {
|
|||||||
if (route.params.characterId) {
|
if (route.params.characterId) {
|
||||||
const id = parseInt(typeof route.params.characterId === 'string' ? route.params.characterId : route.params.characterId[0]);
|
const id = parseInt(typeof route.params.characterId === 'string' ? route.params.characterId : route.params.characterId[0]);
|
||||||
|
|
||||||
character.value = await findCharacterById(id);
|
character.value = await charactersStore.findById(id);
|
||||||
log.info('Loaded character:', character.value);
|
log.info('Loaded character:', character.value);
|
||||||
} else {
|
} else {
|
||||||
character.value = undefined;
|
character.value = undefined;
|
||||||
@@ -80,7 +77,7 @@ watch(useRoute(), async route => {
|
|||||||
<div class="flex-col border-b-1">
|
<div class="flex-col border-b-1">
|
||||||
Rule Book:
|
Rule Book:
|
||||||
<select class="me-2 mb-2 w-50" v-model="ruleBook">
|
<select class="me-2 mb-2 w-50" v-model="ruleBook">
|
||||||
<option v-for="rb in ruleBooks" :key="rb.ruleBookId" :value="rb">{{ rb.name }}</option>
|
<option v-for="rb in ruleBookStore.ruleBooks" :key="rb.ruleBookId" :value="rb">{{ rb.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-col border-b-1">
|
<div class="flex-col border-b-1">
|
||||||
@@ -88,7 +85,7 @@ watch(useRoute(), async route => {
|
|||||||
<div class="flex flex-wrap items-center mb-2 mt-2">
|
<div class="flex flex-wrap items-center mb-2 mt-2">
|
||||||
<div class="me-2" v-for="ref in ledgerRefs" :ref="ref">
|
<div class="me-2" v-for="ref in ledgerRefs" :ref="ref">
|
||||||
<span class="me-1">{{ref}}:</span>
|
<span class="me-1">{{ref}}:</span>
|
||||||
<LedgerSelect :ledgers="ledgersToUse" :modelValue="bindings[ref] ?? systemLedger" @update:modelValue="value => bindings[ref] = value" />
|
<LedgerSelect :ledgers="ledgersToUse" :modelValue="bindings[ref] ?? systemLedger" @update:modelValue="value => { if (value) bindings[ref] = value }" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,39 +1,49 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import {ref, watch} from "vue";
|
import {ref, watch} from "vue";
|
||||||
import {useDebounceFn} from "@vueuse/core";
|
import {useDebounceFn, useEventListener} from "@vueuse/core";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import {activityTypes, RuleInput, Rules, useRuleBooksStore} from "@/rules";
|
import {ScriptEditor, useRuleBooksStore} from "@/rules";
|
||||||
import {PlusIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
import {PlusIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
||||||
import {routeNames} from "@/routes";
|
import {routeNames} from "@/routes";
|
||||||
import {Dropdown} from "@/components";
|
import {SliderCheckbox} from "@/components";
|
||||||
|
|
||||||
const ruleBookId = ref<string>();
|
const ruleBookId = ref<string>();
|
||||||
const name = ref<string>('');
|
const name = ref<string>('');
|
||||||
|
const usedForAcquisitions = ref<boolean>(false);
|
||||||
const ledgerRefs = ref<string[]>([]);
|
const ledgerRefs = ref<string[]>([]);
|
||||||
const rules = ref<Rules>({});
|
const script = ref<string>('');
|
||||||
|
|
||||||
const {findById, create, update, refresh} = useRuleBooksStore();
|
const ruleBooksStore = useRuleBooksStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
if (!ruleBookId.value) {
|
if (!ruleBookId.value) {
|
||||||
const created = await create({
|
const created = await ruleBooksStore.create({
|
||||||
name: name.value,
|
name: name.value,
|
||||||
|
usedForAcquisitions: usedForAcquisitions.value,
|
||||||
ledgerRefs: ledgerRefs.value,
|
ledgerRefs: ledgerRefs.value,
|
||||||
rules: rules.value
|
script: script.value
|
||||||
})
|
})
|
||||||
await router.push({ name: routeNames.editRuleBook, params: {ruleBookId: created.ruleBookId}})
|
await router.push({ name: routeNames.editRuleBook, params: {ruleBookId: created.ruleBookId}})
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
await update(ruleBookId.value, {
|
await ruleBooksStore.update(ruleBookId.value, {
|
||||||
name: name.value,
|
name: name.value,
|
||||||
|
usedForAcquisitions: usedForAcquisitions.value,
|
||||||
ledgerRefs: ledgerRefs.value,
|
ledgerRefs: ledgerRefs.value,
|
||||||
rules: rules.value
|
script: script.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEventListener(window, 'keydown', (event: KeyboardEvent) => {
|
||||||
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 's') {
|
||||||
|
event.preventDefault();
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
}, {capture: true});
|
||||||
|
|
||||||
const addLedgerRef = () => {
|
const addLedgerRef = () => {
|
||||||
ledgerRefs.value = [...ledgerRefs.value, '']
|
ledgerRefs.value = [...ledgerRefs.value, '']
|
||||||
}
|
}
|
||||||
@@ -47,41 +57,47 @@ const removeLedgerRef = (index: number) => {
|
|||||||
|
|
||||||
watch(useRoute(), async route => {
|
watch(useRoute(), async route => {
|
||||||
if (route.params.ruleBookId) {
|
if (route.params.ruleBookId) {
|
||||||
const promise = refresh(); // FIXME don't call refresh
|
const promise = ruleBooksStore.refresh(); // FIXME don't call refresh
|
||||||
|
|
||||||
const id = typeof route.params.ruleBookId === 'string' ? route.params.ruleBookId : route.params.ruleBookId[0];
|
const id = typeof route.params.ruleBookId === 'string' ? route.params.ruleBookId : route.params.ruleBookId[0];
|
||||||
|
|
||||||
await promise;
|
await promise;
|
||||||
|
|
||||||
const ruleBook = findById(id);
|
const ruleBook = ruleBooksStore.findById(id);
|
||||||
|
|
||||||
ruleBookId.value = id;
|
ruleBookId.value = id;
|
||||||
name.value = ruleBook?.name ?? '';
|
name.value = ruleBook?.name ?? '';
|
||||||
ledgerRefs.value = [...ruleBook?.ledgerRefs];
|
usedForAcquisitions.value = ruleBook?.usedForAcquisitions ?? false;
|
||||||
rules.value = {...ruleBook?.rules}; // TODO fully clone rules
|
ledgerRefs.value = [...(ruleBook?.ledgerRefs ?? [])];
|
||||||
|
script.value = ruleBook?.script ?? '';
|
||||||
log.info('Loaded rule book:', ruleBook);
|
log.info('Loaded rule book:', ruleBook);
|
||||||
} else {
|
} else {
|
||||||
ruleBookId.value = undefined;
|
ruleBookId.value = undefined;
|
||||||
name.value = '';
|
name.value = '';
|
||||||
|
usedForAcquisitions.value = false;
|
||||||
ledgerRefs.value = [];
|
ledgerRefs.value = [];
|
||||||
rules.value = {};
|
script.value = '';
|
||||||
log.info('No rule book to load');
|
log.info('No rule book to load');
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="grid mb-2 mt-4">
|
<div class="flex flex-col mb-2 mt-4 h-[calc(100vh-4.5rem)]">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col grow min-h-0">
|
||||||
<div class="flex grow border-b-1">
|
<div class="flex grow border-b-1">
|
||||||
Name:
|
Name:
|
||||||
<input class="mb-2 ms-2" type="text" v-model="name" />
|
<input class="mb-2 ms-2" type="text" v-model="name" />
|
||||||
|
<label class="flex items-center ms-2 mb-2">
|
||||||
|
<SliderCheckbox class="me-2" v-model="usedForAcquisitions" />
|
||||||
|
Used for acquisitions
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-b-1">
|
<div class="border-b-1">
|
||||||
Ledgers References:
|
Ledgers References:
|
||||||
<div class="flex flex-wrap items-center">
|
<div class="flex flex-wrap items-center mt-2">
|
||||||
<div class="flex items-center mb-2" v-for="(ledgerRef, index) in ledgerRefs" :key="index">
|
<div class="flex items-center mb-2" v-for="(ledgerRef, index) in ledgerRefs" :key="index">
|
||||||
<input class="me-1" type="text" :value="ledgerRefs[index]" @input="updateLedgerRef(index, ($event.target as HTMLInputElement).value)" />
|
<input class="me-1" type="text" :value="ledgerRef" @input="updateLedgerRef(index, ($event.target as HTMLInputElement).value)" />
|
||||||
<button class="btn-icon me-2" @click="removeLedgerRef(index)"><TrashIcon /></button>
|
<button class="btn-icon me-2" @click="removeLedgerRef(index)"><TrashIcon /></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center mb-2">
|
<div class="flex items-center mb-2">
|
||||||
@@ -89,14 +105,9 @@ watch(useRoute(), async route => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col grow border-b-1" v-for="activityType in activityTypes" :key="activityType.key">
|
<div class="flex flex-col grow min-h-0 border-b-1">
|
||||||
<Dropdown :inline="true" :autoClose="false" class="rule-dropdown">
|
Script:
|
||||||
<template #button>
|
<ScriptEditor class="mt-2 mb-2" v-model="script" :ledgerRefs="ledgerRefs" />
|
||||||
<span>{{ activityType.name }}</span>
|
|
||||||
</template>
|
|
||||||
<RuleInput :ledgerRefs="ledgerRefs" v-model="rules[activityType.key]" />
|
|
||||||
</Dropdown>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 justify-end flex">
|
<div class="mt-2 justify-end flex">
|
||||||
@@ -106,15 +117,3 @@ watch(useRoute(), async route => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@reference "@/style.css";
|
|
||||||
|
|
||||||
.rule-dropdown :deep(>button) {
|
|
||||||
@apply bg-slate-800 hover:bg-slate-800 border-none flex items-center w-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-dropdown.dropdown-open :deep(>button) {
|
|
||||||
@apply bg-slate-800 rounded-b-none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,18 +1,46 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {storeToRefs} from "pinia";
|
import {Character, CharacterLabel} from "@/characters";
|
||||||
import {CharacterLabel, useCharactersStore} from "@/characters";
|
|
||||||
import {PencilSquareIcon} from "@heroicons/vue/24/outline";
|
import {PencilSquareIcon} from "@heroicons/vue/24/outline";
|
||||||
import {routeNames} from "@/routes";
|
import {CharacterRuleBook, useCharacterRuleBooksStore} from "@/rules";
|
||||||
|
import {routeNames} from "@/routes.ts";
|
||||||
|
import {SortableHeader, useSort} from "@/components/table";
|
||||||
|
|
||||||
const {characters} = storeToRefs(useCharactersStore());
|
type CharacterRuleBookView = {
|
||||||
|
character: Character;
|
||||||
|
characterName: string;
|
||||||
|
ruleBookName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const characterRuleBooksStore = useCharacterRuleBooksStore();
|
||||||
|
|
||||||
|
const { sortedArray, headerProps } = useSort<CharacterRuleBookView>(() => characterRuleBooksStore.characterRuleBooks.map((characterRuleBook: CharacterRuleBook): CharacterRuleBookView => ({
|
||||||
|
character: characterRuleBook.character,
|
||||||
|
characterName: characterRuleBook.character.name,
|
||||||
|
ruleBookName: characterRuleBook.ruleBook.name
|
||||||
|
})))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="grid mb-2 mt-4">
|
<div class="grid mb-2 mt-4">
|
||||||
<div v-for="character in characters" :key="character.characterId" class="flex items-center mb-2">
|
<table>
|
||||||
<CharacterLabel class="flex grow" :character="character" />
|
<thead>
|
||||||
<RouterLink class="btn-icon ms-2" :to="{ name: routeNames.editCharacterRulebook, params: { characterId: character.characterId } }"><PencilSquareIcon /></RouterLink>
|
<tr>
|
||||||
</div>
|
<SortableHeader v-bind="headerProps" sortKey="characterName">Character</SortableHeader>
|
||||||
|
<SortableHeader v-bind="headerProps" sortKey="ruleBookName">Rule Book</SortableHeader>
|
||||||
|
<SortableHeader v-bind="headerProps" sortKey="buttons" unsortable />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="characterRuleBookView in sortedArray" :key="characterRuleBookView.character.characterId" >
|
||||||
|
<td>
|
||||||
|
<CharacterLabel :character="characterRuleBookView.character" />
|
||||||
|
</td>
|
||||||
|
<td>{{characterRuleBookView.ruleBookName}}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<RouterLink class="btn-icon" :to="{ name: routeNames.editCharacterRulebook, params: { characterId: characterRuleBookView.character.characterId } }"><PencilSquareIcon /></RouterLink>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,10 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {storeToRefs} from "pinia";
|
import {DocumentDuplicateIcon, PencilSquareIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
||||||
import {PencilSquareIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
import {confirm} from "@/confirm";
|
||||||
import {useRuleBooksStore} from "@/rules";
|
import {RuleBook, useRuleBooksStore} from "@/rules";
|
||||||
import {routeNames} from "@/routes";
|
import {routeNames} from "@/routes";
|
||||||
|
|
||||||
const {ruleBooks} = storeToRefs(useRuleBooksStore());
|
const ruleBooksStore = useRuleBooksStore();
|
||||||
|
|
||||||
|
const duplicate = async (ruleBook: RuleBook) => {
|
||||||
|
if (await confirm({title: "Duplicate Rule Book", message: `Duplicate ${ruleBook.name}?`, confirmLabel: "Duplicate"})) {
|
||||||
|
await ruleBooksStore.duplicate(ruleBook);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const remove = async (ruleBook: RuleBook) => {
|
||||||
|
if (await confirm({title: "Delete Rule Book", message: `Delete ${ruleBook.name}?`, confirmLabel: "Delete", danger: true})) {
|
||||||
|
await ruleBooksStore.remove(ruleBook.ruleBookId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -13,10 +25,11 @@ const {ruleBooks} = storeToRefs(useRuleBooksStore());
|
|||||||
<div class="flex justify-end border-b-1">
|
<div class="flex justify-end border-b-1">
|
||||||
<RouterLink class="button mb-2 ms-2" :to="{ name: routeNames.newRuleBook}">New Rule Book</RouterLink>
|
<RouterLink class="button mb-2 ms-2" :to="{ name: routeNames.newRuleBook}">New Rule Book</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="ruleBook in ruleBooks" :key="ruleBook.ruleBookId" class="flex items-center mt-2">
|
<div v-for="ruleBook in ruleBooksStore.ruleBooks" :key="ruleBook.ruleBookId" class="flex items-center mt-2">
|
||||||
<span class="flex grow me-2">{{ruleBook.name}}</span>
|
<span class="flex grow me-2">{{ruleBook.name}}</span>
|
||||||
<RouterLink class="btn-icon me-1" :to="{ name: routeNames.editRuleBook, params: { ruleBookId: ruleBook.ruleBookId } }"><PencilSquareIcon /></RouterLink>
|
<RouterLink class="btn-icon me-1" :to="{ name: routeNames.editRuleBook, params: { ruleBookId: ruleBook.ruleBookId } }"><PencilSquareIcon /></RouterLink>
|
||||||
<button class="btn-icon"><TrashIcon /></button>
|
<button class="btn-icon me-1" @click="duplicate(ruleBook)"><DocumentDuplicateIcon /></button>
|
||||||
|
<button class="btn-icon text-amber-700 hover:text-amber-600" @click="remove(ruleBook)"><TrashIcon /></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
+1
-1
@@ -43,7 +43,7 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
{path: '/market', component: () => import('@/pages/Market.vue'), children: [
|
{path: '/market', component: () => import('@/pages/Market.vue'), children: [
|
||||||
{path: '', redirect: {name: routeNames.marketTypes}},
|
{path: '', redirect: {name: routeNames.marketTypes}},
|
||||||
{path: 'types/:type?', name: routeNames.marketTypes, component: () => import('@/pages/market/TypeInfo.vue')},
|
{path: 'types/:type?', name: routeNames.marketTypes, component: () => import('@/pages/market/TypeInfo.vue')},
|
||||||
{path: 'tracking', component: () => import('@/pages/market/Tracking.vue')},
|
{path: 'scan', component: () => import('@/pages/market/Scan.vue')},
|
||||||
{path: 'acquisitions', component: () => import('@/pages/market/Acquisitions.vue')},
|
{path: 'acquisitions', component: () => import('@/pages/market/Acquisitions.vue')},
|
||||||
]},
|
]},
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
import {systemLedger, systemLedgerRef} from "@/ledger";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
ledgerRefs: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
defineProps<Props>()
|
|
||||||
|
|
||||||
const ledgerRef = defineModel<string>();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<select v-model="ledgerRef" :class="{'system-ledger': ledgerRef === systemLedgerRef}">
|
|
||||||
<option v-for="l in ledgerRefs" :key="l" :value="l" :class="{'system-ledger': l === systemLedgerRef}">{{ l === systemLedgerRef ? systemLedger.name : l }}</option>
|
|
||||||
</select>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@reference "@/style.css";
|
|
||||||
|
|
||||||
.system-ledger {
|
|
||||||
@apply text-emerald-400;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
import {RuleClauseResponse} from "@/generated/mammon";
|
|
||||||
import {computed, watch} from "vue";
|
|
||||||
import {systemLedgerRef} from "@/ledger";
|
|
||||||
import {ratesTypes} from "@/rules/rules.ts";
|
|
||||||
import LedgerRefSelect from "./LedgerRefSelect.vue";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
ledgerRefs: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
|
|
||||||
const rule = defineModel<RuleClauseResponse>({ default: {
|
|
||||||
rate: ratesTypes.None,
|
|
||||||
fromLedgerRef: systemLedgerRef,
|
|
||||||
toLedgerRef: systemLedgerRef,
|
|
||||||
}});
|
|
||||||
|
|
||||||
const ledgerRefsWithSystem = computed<string[]>(() => [systemLedgerRef, ...props.ledgerRefs])
|
|
||||||
|
|
||||||
watch(ledgerRefsWithSystem, (newVal, oldVal) => {
|
|
||||||
if (newVal.length !== oldVal.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule.value.fromLedgerRef && rule.value.fromLedgerRef !== systemLedgerRef) {
|
|
||||||
rule.value.fromLedgerRef = newVal[oldVal.findIndex(v => v === rule.value.fromLedgerRef)]
|
|
||||||
}
|
|
||||||
if (rule.value.toLedgerRef && rule.value.toLedgerRef !== systemLedgerRef) {
|
|
||||||
rule.value.toLedgerRef = newVal[oldVal.findIndex(v => v === rule.value.toLedgerRef)]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
From:
|
|
||||||
<LedgerRefSelect class="me-2 grow" v-model="rule.fromLedgerRef" :ledger-refs="ledgerRefsWithSystem"/>
|
|
||||||
To:
|
|
||||||
<LedgerRefSelect class="me-2 grow" v-model="rule.toLedgerRef" :ledger-refs="ledgerRefsWithSystem"/>
|
|
||||||
At:
|
|
||||||
<select class="me-2 grow" v-model="rule.rate">
|
|
||||||
<option v-for="rateType in ratesTypes" :key="rateType.key" :value="rateType.key">{{ rateType.name }}</option>
|
|
||||||
</select>
|
|
||||||
</template>
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
import {RuleClauseResponse, RuleClauseResponseRateEnum, RuleResponse} from "@/generated/mammon";
|
|
||||||
import RuleClauseInput from "@/rules/RuleClauseInput.vue";
|
|
||||||
import {computed, useTemplateRef} from "vue";
|
|
||||||
import {Bars4Icon, PlusIcon, TrashIcon} from '@heroicons/vue/24/outline';
|
|
||||||
import {useSortable} from "@vueuse/integrations/useSortable";
|
|
||||||
import {systemLedgerRef} from "@/ledger";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
ledgerRefs: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
|
|
||||||
const rule = defineModel<RuleResponse>({default: {clauses:[]}});
|
|
||||||
const clauses = computed<RuleClauseResponse[]>({
|
|
||||||
get: () => rule.value && rule.value.clauses ? rule.value.clauses : [],
|
|
||||||
set: value => rule.value = {clauses: value}
|
|
||||||
})
|
|
||||||
|
|
||||||
const addClause = () => {
|
|
||||||
clauses.value = [...clauses.value, {
|
|
||||||
rate: RuleClauseResponseRateEnum.None,
|
|
||||||
fromLedgerRef: systemLedgerRef,
|
|
||||||
toLedgerRef: systemLedgerRef
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
const setClause = (index: number, clause?: RuleClauseResponse) => {
|
|
||||||
if (!clause) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
clauses.value = clauses.value.with(index, clause)
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeClause = (index: number) => {
|
|
||||||
clauses.value = clauses.value.toSpliced(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const sortableContainer = useTemplateRef('sortable-container')
|
|
||||||
useSortable(sortableContainer, clauses, { handle: '.sortable-handle'});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex-col">
|
|
||||||
<div ref="sortable-container" class="flex-col">
|
|
||||||
<div class="flex items-end gap-2 mt-2" v-for="(clause, index) in clauses" :key="index">
|
|
||||||
<span class="sortable-handle flex">
|
|
||||||
<Bars4Icon class="w-6"/>
|
|
||||||
</span>
|
|
||||||
<RuleClauseInput :ledgerRefs="ledgerRefs" :modelValue="clause" @update:modelValue="v => setClause(index, v)" />
|
|
||||||
<button class="btn-icon" @click="removeClause(index)"><TrashIcon /></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-end mb-2 mt-2">
|
|
||||||
<button class="btn-icon" @click="addClause"><PlusIcon /></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@reference "@/style.css";
|
|
||||||
|
|
||||||
.sortable-handle {
|
|
||||||
@apply cursor-grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sortable-chosen {
|
|
||||||
@apply cursor-grabbing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sortable-chosen .sortable-handle {
|
|
||||||
@apply cursor-grabbing;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
||||||
|
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
||||||
|
import {onBeforeUnmount, onMounted, ref, watch} from 'vue';
|
||||||
|
import {fetchScriptDefinitions} from './rules';
|
||||||
|
|
||||||
|
(self as unknown as { MonacoEnvironment: { getWorker(workerId: string, label: string): Worker } }).MonacoEnvironment = {
|
||||||
|
getWorker(_workerId: string, label: string) {
|
||||||
|
if (label === 'typescript' || label === 'javascript') {
|
||||||
|
return new tsWorker();
|
||||||
|
}
|
||||||
|
return new editorWorker();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let extraLibLoaded = false;
|
||||||
|
|
||||||
|
const loadScriptDefinitions = async () => {
|
||||||
|
if (extraLibLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const definitions = await fetchScriptDefinitions();
|
||||||
|
monaco.typescript.javascriptDefaults.addExtraLib(definitions, 'ts:rule-runner.d.ts');
|
||||||
|
extraLibLoaded = true;
|
||||||
|
} catch {
|
||||||
|
// type definitions are optional — the editor still works without autocomplete
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = defineProps<{ ledgerRefs?: string[] }>();
|
||||||
|
|
||||||
|
let ledgersLib: monaco.IDisposable | undefined;
|
||||||
|
|
||||||
|
const updateLedgerRefs = (refs: readonly string[]) => {
|
||||||
|
ledgersLib?.dispose();
|
||||||
|
const members = refs
|
||||||
|
.filter(ref => ref && ref !== 'system')
|
||||||
|
.map(ref => ` readonly ${JSON.stringify(ref)}: Ledger;`)
|
||||||
|
.join('\n');
|
||||||
|
ledgersLib = monaco.typescript.javascriptDefaults.addExtraLib(
|
||||||
|
`declare interface Ledgers {\n${members}\n}\n`,
|
||||||
|
'ts:rule-runner.ledgers.d.ts'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const model = defineModel<string>({default: ''});
|
||||||
|
const container = ref<HTMLElement>();
|
||||||
|
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await loadScriptDefinitions();
|
||||||
|
updateLedgerRefs(props.ledgerRefs ?? []);
|
||||||
|
|
||||||
|
if (!container.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor = monaco.editor.create(container.value, {
|
||||||
|
value: model.value,
|
||||||
|
language: 'javascript',
|
||||||
|
theme: 'vs-dark',
|
||||||
|
automaticLayout: true,
|
||||||
|
minimap: {enabled: false},
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
fontSize: 13,
|
||||||
|
tabSize: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.onDidChangeModelContent(() => {
|
||||||
|
const value = editor!.getValue();
|
||||||
|
|
||||||
|
if (value !== model.value) {
|
||||||
|
model.value = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(model, value => {
|
||||||
|
if (editor && value !== editor.getValue()) {
|
||||||
|
editor.setValue(value ?? '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.ledgerRefs, refs => updateLedgerRefs(refs ?? []), {deep: true});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
editor?.dispose();
|
||||||
|
ledgersLib?.dispose();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="container" class="script-editor"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.script-editor {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 12rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
export * from "./rules";
|
export * from "./rules";
|
||||||
|
|
||||||
export {default as RuleInput} from './RuleInput.vue';
|
export {default as ScriptEditor} from './ScriptEditor.vue';
|
||||||
+43
-32
@@ -3,36 +3,12 @@ import {
|
|||||||
CharacterRuleBookResponse,
|
CharacterRuleBookResponse,
|
||||||
CreateRuleBookRequest,
|
CreateRuleBookRequest,
|
||||||
RuleBookResponse,
|
RuleBookResponse,
|
||||||
RuleClauseResponseRateEnum,
|
|
||||||
RuleResponse,
|
|
||||||
SetCharacterRuleBookRequest
|
SetCharacterRuleBookRequest
|
||||||
} from "@/generated/mammon";
|
} from "@/generated/mammon";
|
||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
import {ref, triggerRef} from "vue";
|
import {ref, triggerRef} from "vue";
|
||||||
|
|
||||||
export const activityTypes = {
|
export type RuleBook = RuleBookResponse;
|
||||||
itemBought: {key: "ITEM_BOUGHT", name: "Item Bought"},
|
|
||||||
itemSold: {key: "ITEM_SOLD", name: "Item Sold"},
|
|
||||||
itemAcquiredManually: {key: "ITEM_ACQUIRED_MANUALLY", name: "Item Acquired Manually"},
|
|
||||||
itemConsumedManually: {key: "ITEM_CONSUME_MANUALLY", name: "Item Consumed Manually"},
|
|
||||||
bountyEarned: {key: "BOUNTY_EARNED", name: "Bounty Earned"},
|
|
||||||
// itemManufactured: {id: "ITEM_MANUFACTURED", name: "Item Manufactured"}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export type Activity = { key: ActivityType, name: string }
|
|
||||||
export type ActivityType = typeof activityTypes[keyof typeof activityTypes]['key'];
|
|
||||||
export type Rules = { [key: ActivityType]: RuleResponse; };
|
|
||||||
export type RuleBook = RuleBookResponse & { rules: Rules }
|
|
||||||
|
|
||||||
export const ratesTypes = {
|
|
||||||
None: {key: "NONE", name: "0 ISK"},
|
|
||||||
Value: {key: "VALUE", name: "Value"},
|
|
||||||
JitaBuy: {key: "JITA_BUY", name: "Jita Buy Order"},
|
|
||||||
JitaSell: {key: "JITA_SELL", name: "Jita Sell Order"},
|
|
||||||
EveEstimate: {key: "EVE_ESTIMATE", name: "Eve Estimate"},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export type Rate = { key: RuleClauseResponseRateEnum, name: string }
|
|
||||||
|
|
||||||
export const useRuleBooksStore = defineStore('rule-books', () => {
|
export const useRuleBooksStore = defineStore('rule-books', () => {
|
||||||
const ruleBooks = ref<RuleBook[]>([]);
|
const ruleBooks = ref<RuleBook[]>([]);
|
||||||
@@ -56,17 +32,52 @@ export const useRuleBooksStore = defineStore('rule-books', () => {
|
|||||||
const findById = (ruleBookId: string): RuleBook | undefined => ruleBooks.value.find(rb => rb.ruleBookId === ruleBookId);
|
const findById = (ruleBookId: string): RuleBook | undefined => ruleBooks.value.find(rb => rb.ruleBookId === ruleBookId);
|
||||||
const create = (ruleBook: CreateRuleBookRequest) => ruleBookApi.createRuleBook(ruleBook).then(response => addRuleBook(response.data));
|
const create = (ruleBook: CreateRuleBookRequest) => ruleBookApi.createRuleBook(ruleBook).then(response => addRuleBook(response.data));
|
||||||
const update = (ruleBookId: string, ruleBook: CreateRuleBookRequest) => ruleBookApi.updateRuleBook(ruleBookId, ruleBook).then(response => replaceRuleBook(response.data));
|
const update = (ruleBookId: string, ruleBook: CreateRuleBookRequest) => ruleBookApi.updateRuleBook(ruleBookId, ruleBook).then(response => replaceRuleBook(response.data));
|
||||||
|
const duplicate = (ruleBook: RuleBook) => create({
|
||||||
|
name: `${ruleBook.name} (copy)`,
|
||||||
|
usedForAcquisitions: ruleBook.usedForAcquisitions,
|
||||||
|
ledgerRefs: [...ruleBook.ledgerRefs],
|
||||||
|
script: ruleBook.script,
|
||||||
|
});
|
||||||
|
|
||||||
const refresh = () => ruleBookApi.findAllRuleBooks().then(response => ruleBooks.value = response.data as RuleBook[]);
|
const remove = (ruleBookId: string) => ruleBookApi.deleteRuleBook(ruleBookId).then(() => {
|
||||||
|
ruleBooks.value = ruleBooks.value.filter(rb => rb.ruleBookId !== ruleBookId);
|
||||||
|
});
|
||||||
|
|
||||||
|
const refresh = () => ruleBookApi.findAllRuleBooks().then(response => ruleBooks.value = response.data);
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
return {ruleBooks, findById, create, update, refresh};
|
return {ruleBooks, findById, create, update, duplicate, remove, refresh};
|
||||||
})
|
})
|
||||||
|
|
||||||
export const findCharacterRuleBookByCharacterId = (characterId: number): Promise<CharacterRuleBookResponse> => characterRuleBookApi.findCharacterRuleBookByCharacterId(characterId)
|
export type CharacterRuleBook = CharacterRuleBookResponse;
|
||||||
.then(response => response.data)
|
|
||||||
.catch(() => ({characterId, rules: {}}));
|
|
||||||
|
|
||||||
export const setCharacterRuleBookForCharacter = (characterId: number, ruleBook: SetCharacterRuleBookRequest): Promise<CharacterRuleBookResponse> => characterRuleBookApi.setCharacterRuleBookForCharacter(characterId, ruleBook)
|
export const useCharacterRuleBooksStore = defineStore('character-rule-books', () => {
|
||||||
.then(response => response.data);
|
const characterRuleBooks = ref<CharacterRuleBook[]>([]);
|
||||||
|
|
||||||
|
const replaceCharacterRuleBook = (characterRuleBook: CharacterRuleBook) => {
|
||||||
|
const index = characterRuleBooks.value.findIndex(crb => crb.character.characterId === characterRuleBook.character.characterId);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
characterRuleBooks.value[index] = characterRuleBook;
|
||||||
|
} else {
|
||||||
|
characterRuleBooks.value.push(characterRuleBook);
|
||||||
|
}
|
||||||
|
triggerRef(characterRuleBooks);
|
||||||
|
return characterRuleBook;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findByCharacterId = (characterId: number): CharacterRuleBook | undefined => characterRuleBooks.value.find(crb => crb.character.characterId === characterId);
|
||||||
|
|
||||||
|
const setForCharacter = (characterId: number, ruleBook: SetCharacterRuleBookRequest) => characterRuleBookApi.setCharacterRuleBookForCharacter(characterId, ruleBook)
|
||||||
|
.then(response => replaceCharacterRuleBook(response.data));
|
||||||
|
|
||||||
|
const refresh = () => characterRuleBookApi.findAllCharacterRuleBooks().then(response => characterRuleBooks.value = response.data);
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
return {characterRuleBooks, findByCharacterId, setForCharacter, refresh};
|
||||||
|
})
|
||||||
|
|
||||||
|
export const fetchScriptDefinitions = (): Promise<string> =>
|
||||||
|
ruleBookApi.getScriptDefinitions({responseType: 'text'}).then(response => response.data);
|
||||||
+1
-11
@@ -1,5 +1,4 @@
|
|||||||
import axios, { AxiosInstance } from 'axios';
|
import { AxiosInstance } from 'axios';
|
||||||
import rateLimit from 'axios-rate-limit';
|
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
|
|
||||||
export const logResource = (a: AxiosInstance) => {
|
export const logResource = (a: AxiosInstance) => {
|
||||||
@@ -14,12 +13,3 @@ export const logResource = (a: AxiosInstance) => {
|
|||||||
return Promise.reject(e);
|
return Promise.reject(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const esiAxiosInstance = rateLimit(axios.create({
|
|
||||||
baseURL: import.meta.env.VITE_ESI_URL,
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
}), { maxRPS: 10 })
|
|
||||||
logResource(esiAxiosInstance)
|
|
||||||
|
|||||||
@@ -1,40 +1,42 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {Transfer, TransferTypes} from "@/transaction/transaction.ts";
|
import {TransferTypes} from "@/transaction/transaction.ts";
|
||||||
import {LedgerLabel, systemLedger, useLedgersStore} from "@/ledger";
|
import {LedgerLabel, systemLedger, useLedgersStore} from "@/ledger";
|
||||||
import {getMarketType, IskLabel, MarketTypeLabel} from "@/market";
|
import {getMarketType, IskLabel, MarketTypeLabel} from "@/market";
|
||||||
import {computedAsync} from "@vueuse/core";
|
import {computedAsync} from "@vueuse/core";
|
||||||
|
import {TransferResponse} from "@/generated/mammon";
|
||||||
|
|
||||||
type TransferWithValue = Transfer & { marketTypeId: number; };
|
type TransferWithValue = TransferResponse & { marketTypeId: number; };
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
transfers?: Transfer[]
|
transfers?: TransferResponse[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const {findById} = useLedgersStore();
|
const ledgersStore = useLedgersStore();
|
||||||
|
|
||||||
const sortedArray = computedAsync(async () => {
|
const sortedArray = computedAsync(async () => {
|
||||||
if (!props.transfers) {
|
if (!props.transfers) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Promise.all(props.transfers.map(async (transfer: TransferWithValue) => {
|
return (await Promise.all(props.transfers.map(async (transfer: TransferWithValue, index) => {
|
||||||
const fromLedger = findById(transfer.fromLedgerId) ?? systemLedger
|
const fromLedger = ledgersStore.findById(transfer.fromLedgerId) ?? systemLedger
|
||||||
const toLedger = findById(transfer.toLedgerId) ?? systemLedger
|
const toLedger = ledgersStore.findById(transfer.toLedgerId) ?? systemLedger
|
||||||
|
|
||||||
const item = transfer.marketTypeId ? await getMarketType(transfer.marketTypeId) : undefined;
|
const item = transfer.marketTypeId ? await getMarketType(transfer.marketTypeId) : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...transfer,
|
...transfer,
|
||||||
|
order: index,
|
||||||
fromLedger,
|
fromLedger,
|
||||||
toLedger,
|
toLedger,
|
||||||
itemName: item ? item.name : '',
|
itemName: item ? item.name : '',
|
||||||
fromLedgerName: fromLedger.name,
|
fromLedgerName: fromLedger.name,
|
||||||
toLedgerName: toLedger.name
|
toLedgerName: toLedger.name
|
||||||
}
|
}
|
||||||
}))
|
}))).sort((a, b) => a.order - b.order)
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import {TransactionResponseTransfersInner, TransferResponseTypeEnum} from "@/generated/mammon";
|
import {TransferResponse} from "@/generated/mammon";
|
||||||
|
|
||||||
export const TransferTypes = TransferResponseTypeEnum;
|
export const TransferTypes = {
|
||||||
export type TransferType = TransferResponseTypeEnum;
|
Isk: 'ISK',
|
||||||
export type Transfer = TransactionResponseTransfersInner;
|
Item: 'ITEM',
|
||||||
|
} as const;
|
||||||
|
export type TransferType = TransferResponse['type'];
|
||||||
|
|||||||
Reference in New Issue
Block a user