character rule book store

This commit is contained in:
Sirttas
2026-06-11 19:42:56 +02:00
parent 9cd0d5fb5e
commit f3cb4798d5
5 changed files with 540 additions and 55 deletions
+218 -21
View File
@@ -57,7 +57,10 @@ 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: delete:
tags: tags:
- rule-book - rule-book
@@ -75,7 +78,10 @@ paths:
"204": "204":
description: The rule book was deleted description: The rule book was deleted
"400": "400":
description: The rule book is associated to a character 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:
@@ -105,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}:
@@ -137,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:
@@ -193,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:
@@ -225,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:
@@ -256,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:
@@ -278,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:
@@ -326,6 +363,86 @@ paths:
text/plain: text/plain:
schema: schema:
type: string type: string
/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: string
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/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: string
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: string
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: string
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
/ledgers: /ledgers:
get: get:
tags: tags:
@@ -429,6 +546,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:
@@ -560,15 +692,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:
@@ -576,7 +717,18 @@ 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
@@ -617,6 +769,62 @@ components:
required: required:
- memberLedgerIds - memberLedgerIds
- name - name
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
MarketScanResponse:
type: object
properties:
marketTypeId:
type: integer
format: int64
q1:
type: number
median:
type: number
q3:
type: number
totalVolume:
type: integer
format: int64
profit:
type: number
score:
type: number
required:
- marketTypeId
- median
- profit
- q1
- q3
- score
- totalVolume
LedgerResponse: LedgerResponse:
discriminator: discriminator:
propertyName: type propertyName: type
@@ -732,17 +940,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:
+279 -2
View File
@@ -50,8 +50,8 @@ export interface CharacterResponse {
'name': string; 'name': string;
} }
export interface CharacterRuleBookResponse { export interface CharacterRuleBookResponse {
'characterId': number; 'character': CharacterResponse;
'ruleBookId': string; 'ruleBook': RuleBookSummaryResponse;
'bindings': { [key: string]: string; }; 'bindings': { [key: string]: string; };
} }
export interface CombinedLedgerResponse extends LedgerResponse { export interface CombinedLedgerResponse extends LedgerResponse {
@@ -98,6 +98,24 @@ export interface MainLedgerResponse extends LedgerResponse {
'name': string; 'name': string;
'balance': number; 'balance': number;
} }
export interface MarketHistoryResponse {
'marketTypeId': number;
'date': string;
'average': number;
'highest': number;
'lowest': number;
'orderCount': number;
'volume': number;
}
export interface MarketScanResponse {
'marketTypeId': number;
'q1': number;
'median': number;
'q3': number;
'totalVolume': number;
'profit': number;
'score': number;
}
export interface RuleBookResponse { export interface RuleBookResponse {
'ruleBookId': string; 'ruleBookId': string;
'name': string; 'name': string;
@@ -105,6 +123,10 @@ export interface RuleBookResponse {
'ledgerRefs': Array<string>; 'ledgerRefs': Array<string>;
'script': string; 'script': string;
} }
export interface RuleBookSummaryResponse {
'ruleBookId': string;
'name': string;
}
export interface SetCharacterRuleBookRequest { export interface SetCharacterRuleBookRequest {
'ruleBookId': string; 'ruleBookId': string;
'bindings': { [key: string]: string; }; 'bindings': { [key: string]: string; };
@@ -488,6 +510,36 @@ export class CharacterApi extends BaseAPI {
*/ */
export const CharacterRuleBookApiAxiosParamCreator = function (configuration?: Configuration) { export const CharacterRuleBookApiAxiosParamCreator = function (configuration?: Configuration) {
return { return {
/**
*
* @summary Find the rule books of all characters that have a token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
findAllCharacterRuleBooks: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/characters/rule-books`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
localVarHeaderParameter['Accept'] = '*/*';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/** /**
* *
* @summary Find the rule book assigned to a character * @summary Find the rule book assigned to a character
@@ -570,6 +622,18 @@ export const CharacterRuleBookApiAxiosParamCreator = function (configuration?: C
export const CharacterRuleBookApiFp = function(configuration?: Configuration) { export const CharacterRuleBookApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = CharacterRuleBookApiAxiosParamCreator(configuration) const localVarAxiosParamCreator = CharacterRuleBookApiAxiosParamCreator(configuration)
return { return {
/**
*
* @summary Find the rule books of all characters that have a token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async findAllCharacterRuleBooks(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<CharacterRuleBookResponse>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.findAllCharacterRuleBooks(options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['CharacterRuleBookApi.findAllCharacterRuleBooks']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/** /**
* *
* @summary Find the rule book assigned to a character * @summary Find the rule book assigned to a character
@@ -606,6 +670,15 @@ export const CharacterRuleBookApiFp = function(configuration?: Configuration) {
export const CharacterRuleBookApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { export const CharacterRuleBookApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = CharacterRuleBookApiFp(configuration) const localVarFp = CharacterRuleBookApiFp(configuration)
return { return {
/**
*
* @summary Find the rule books of all characters that have a token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
findAllCharacterRuleBooks(options?: RawAxiosRequestConfig): AxiosPromise<Array<CharacterRuleBookResponse>> {
return localVarFp.findAllCharacterRuleBooks(options).then((request) => request(axios, basePath));
},
/** /**
* *
* @summary Find the rule book assigned to a character * @summary Find the rule book assigned to a character
@@ -634,6 +707,16 @@ export const CharacterRuleBookApiFactory = function (configuration?: Configurati
* CharacterRuleBookApi - object-oriented interface * CharacterRuleBookApi - object-oriented interface
*/ */
export class CharacterRuleBookApi extends BaseAPI { export class CharacterRuleBookApi extends BaseAPI {
/**
*
* @summary Find the rule books of all characters that have a token
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public findAllCharacterRuleBooks(options?: RawAxiosRequestConfig) {
return CharacterRuleBookApiFp(this.configuration).findAllCharacterRuleBooks(options).then((request) => request(this.axios, this.basePath));
}
/** /**
* *
* @summary Find the rule book assigned to a character * @summary Find the rule book assigned to a character
@@ -1180,6 +1263,200 @@ export class LedgerApi extends BaseAPI {
/**
* MarketApi - axios parameter creator
*/
export const MarketApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
*
* @summary Find the market history of a type, most recent first
* @param {number} marketTypeId Id of the market type
* @param {string} [days] Optional number of most recent days to return; omit for the full history
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
findHistory: async (marketTypeId: number, days?: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'marketTypeId' is not null or undefined
assertParamExists('findHistory', 'marketTypeId', marketTypeId)
const localVarPath = `/market/{marketTypeId}/history`
.replace('{marketTypeId}', encodeURIComponent(String(marketTypeId)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
if (days !== undefined) {
localVarQueryParameter['days'] = days;
}
localVarHeaderParameter['Accept'] = '*/*';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary Scan every tracked market type, returning volume-weighted price quartiles for each
* @param {string} [days] Number of most recent days of history to analyse
* @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders
* @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
scanMarket: async (days?: string, brokerFee?: string, salesTax?: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/market/scan`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
if (days !== undefined) {
localVarQueryParameter['days'] = days;
}
if (brokerFee !== undefined) {
localVarQueryParameter['brokerFee'] = brokerFee;
}
if (salesTax !== undefined) {
localVarQueryParameter['salesTax'] = salesTax;
}
localVarHeaderParameter['Accept'] = '*/*';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
}
};
/**
* MarketApi - functional programming interface
*/
export const MarketApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = MarketApiAxiosParamCreator(configuration)
return {
/**
*
* @summary Find the market history of a type, most recent first
* @param {number} marketTypeId Id of the market type
* @param {string} [days] Optional number of most recent days to return; omit for the full history
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async findHistory(marketTypeId: number, days?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MarketHistoryResponse>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.findHistory(marketTypeId, days, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['MarketApi.findHistory']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
*
* @summary Scan every tracked market type, returning volume-weighted price quartiles for each
* @param {string} [days] Number of most recent days of history to analyse
* @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders
* @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async scanMarket(days?: string, brokerFee?: string, salesTax?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MarketScanResponse>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.scanMarket(days, brokerFee, salesTax, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['MarketApi.scanMarket']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
}
};
/**
* MarketApi - factory interface
*/
export const MarketApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = MarketApiFp(configuration)
return {
/**
*
* @summary Find the market history of a type, most recent first
* @param {number} marketTypeId Id of the market type
* @param {string} [days] Optional number of most recent days to return; omit for the full history
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
findHistory(marketTypeId: number, days?: string, options?: RawAxiosRequestConfig): AxiosPromise<Array<MarketHistoryResponse>> {
return localVarFp.findHistory(marketTypeId, days, options).then((request) => request(axios, basePath));
},
/**
*
* @summary Scan every tracked market type, returning volume-weighted price quartiles for each
* @param {string} [days] Number of most recent days of history to analyse
* @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders
* @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
scanMarket(days?: string, brokerFee?: string, salesTax?: string, options?: RawAxiosRequestConfig): AxiosPromise<Array<MarketScanResponse>> {
return localVarFp.scanMarket(days, brokerFee, salesTax, options).then((request) => request(axios, basePath));
},
};
};
/**
* MarketApi - object-oriented interface
*/
export class MarketApi extends BaseAPI {
/**
*
* @summary Find the market history of a type, most recent first
* @param {number} marketTypeId Id of the market type
* @param {string} [days] Optional number of most recent days to return; omit for the full history
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public findHistory(marketTypeId: number, days?: string, options?: RawAxiosRequestConfig) {
return MarketApiFp(this.configuration).findHistory(marketTypeId, days, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary Scan every tracked market type, returning volume-weighted price quartiles for each
* @param {string} [days] Number of most recent days of history to analyse
* @param {string} [brokerFee] Broker fee as a fraction (e.g. 0.015 for 1.5%), paid on both buy and sell orders
* @param {string} [salesTax] Sales tax as a fraction (e.g. 0.036 for 3.6%), paid on sell orders
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public scanMarket(days?: string, brokerFee?: string, salesTax?: string, options?: RawAxiosRequestConfig) {
return MarketApiFp(this.configuration).scanMarket(days, brokerFee, salesTax, options).then((request) => request(this.axios, this.basePath));
}
}
/** /**
* ProcessingApi - axios parameter creator * ProcessingApi - axios parameter creator
*/ */
+6 -6
View File
@@ -4,9 +4,8 @@ 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 {storeToRefs} from "pinia";
@@ -17,6 +16,7 @@ type Bindings = { [key: string]: Ledger; };
const ruleBookStore = useRuleBooksStore(); const ruleBookStore = useRuleBooksStore();
const {findById: findRuleBookById} = ruleBookStore; const {findById: findRuleBookById} = ruleBookStore;
const {ruleBooks} = storeToRefs(ruleBookStore); const {ruleBooks} = storeToRefs(ruleBookStore);
const {findByCharacterId, setForCharacter} = useCharacterRuleBooksStore();
const {findById: findCharacterById} = useCharactersStore(); const {findById: findCharacterById} = useCharactersStore();
const {ledgers} = storeToRefs(useLedgersStore()); const {ledgers} = storeToRefs(useLedgersStore());
@@ -31,11 +31,11 @@ watchEffect(async () => {
const characterId = character.value?.characterId; const characterId = character.value?.characterId;
if (characterId) { if (characterId) {
const characterRuleBook = await findCharacterRuleBookByCharacterId(characterId); const characterRuleBook = findByCharacterId(characterId);
ruleBook.value = findRuleBookById(characterRuleBook.ruleBookId); ruleBook.value = findRuleBookById(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 +46,7 @@ const save = () => {
const ruleBookId = ruleBook.value?.ruleBookId; const ruleBookId = ruleBook.value?.ruleBookId;
if (characterId && ruleBookId) { if (characterId && ruleBookId) {
setCharacterRuleBookForCharacter(characterId, { setForCharacter(characterId, {
ruleBookId, ruleBookId,
bindings: Object.fromEntries( bindings: Object.fromEntries(
Object.entries(bindings.value) Object.entries(bindings.value)
+10 -21
View File
@@ -1,34 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import {storeToRefs} from "pinia"; import {Character, CharacterLabel} from "@/characters";
import {Character, CharacterLabel, useCharactersStore} from "@/characters";
import {PencilSquareIcon} from "@heroicons/vue/24/outline"; import {PencilSquareIcon} from "@heroicons/vue/24/outline";
import {findCharacterRuleBookByCharacterId, useRuleBooksStore} from "@/rules"; import {CharacterRuleBook, useCharacterRuleBooksStore} from "@/rules";
import {computedAsync} from "@vueuse/core";
import {routeNames} from "@/routes.ts"; import {routeNames} from "@/routes.ts";
import {SortableHeader, useSort} from "@/components/table"; import {SortableHeader, useSort} from "@/components/table";
type CharacterRuleBookView = { type CharacterRuleBookView = {
character: Character; character: Character;
characterName: string; characterName: string;
characterId: number;
ruleBookName: string; ruleBookName: string;
} }
const {characters} = storeToRefs(useCharactersStore()); const characterRuleBooksStore = useCharacterRuleBooksStore();
const {findById: findRuleBookById} = useRuleBooksStore();
const { sortedArray, headerProps } = useSort<CharacterRuleBookView>(() => characterRuleBooksStore.characterRuleBooks.map((characterRuleBook: CharacterRuleBook): CharacterRuleBookView => ({
const { sortedArray, headerProps } = useSort(computedAsync<CharacterRuleBookView[]>(async () => await Promise.all(characters.value.map(async (character: Character): Promise<CharacterRuleBookView> => { character: characterRuleBook.character,
const characterRuleBook = await findCharacterRuleBookByCharacterId(character.characterId); characterName: characterRuleBook.character.name,
const ruleBook = findRuleBookById(characterRuleBook.ruleBookId); ruleBookName: characterRuleBook.ruleBook.name
})))
return {
character,
characterName: character.name,
characterId: character.characterId,
ruleBookName: ruleBook?.name ?? ''
}
})), []))
</script> </script>
<template> <template>
@@ -42,13 +31,13 @@ const { sortedArray, headerProps } = useSort(computedAsync<CharacterRuleBookView
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="characterRuleBookView in sortedArray" :key="characterRuleBookView.characterId" > <tr v-for="characterRuleBookView in sortedArray" :key="characterRuleBookView.character.characterId" >
<td> <td>
<CharacterLabel :character="characterRuleBookView.character" /> <CharacterLabel :character="characterRuleBookView.character" />
</td> </td>
<td>{{characterRuleBookView.ruleBookName}}</td> <td>{{characterRuleBookView.ruleBookName}}</td>
<td class="text-right"> <td class="text-right">
<RouterLink class="btn-icon" :to="{ name: routeNames.editCharacterRulebook, params: { characterId: characterRuleBookView.characterId } }"><PencilSquareIcon /></RouterLink> <RouterLink class="btn-icon" :to="{ name: routeNames.editCharacterRulebook, params: { characterId: characterRuleBookView.character.characterId } }"><PencilSquareIcon /></RouterLink>
</td> </td>
</tr> </tr>
</tbody> </tbody>
+27 -5
View File
@@ -50,12 +50,34 @@ export const useRuleBooksStore = defineStore('rule-books', () => {
return {ruleBooks, findById, create, update, duplicate, remove, 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, ruleBookId: '', bindings: {}}));
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> => export const fetchScriptDefinitions = (): Promise<string> =>
ruleBookApi.getScriptDefinitions({responseType: 'text'}).then(response => response.data); ruleBookApi.getScriptDefinitions({responseType: 'text'}).then(response => response.data);