makes adding loot working

This commit is contained in:
2019-10-07 15:10:35 +02:00
parent 4f6970c423
commit 70eed30bee
8 changed files with 118 additions and 90 deletions

View File

@@ -138,7 +138,7 @@ impl<'q> AsPlayer<'q> {
if let Ok((item, diff)) = self.conn.transaction(|| { if let Ok((item, diff)) = self.conn.transaction(|| {
use schema::looted::dsl::*; use schema::looted::dsl::*;
let item = schema::items::table.find(item_id).first::<models::Item>(self.conn)?; let item = schema::items::table.find(item_id).first::<models::Item>(self.conn)?;
let new_item = models::item::NewLoot::to_player(self.id, (&item.name, item.base_price)); let new_item = models::item::NewLoot::to_player(self.id, &item);
diesel::insert_into(schema::looted::table) diesel::insert_into(schema::looted::table)
.values(&new_item) .values(&new_item)
.execute(self.conn)?; .execute(self.conn)?;
@@ -285,8 +285,8 @@ impl<'q> AsAdmin<'q> {
/// ///
/// # Params /// # Params
/// List of (name, base_price) values for the new items /// List of (name, base_price) values for the new items
pub fn add_loot(self, items: Vec<(&str, i32)>) -> ActionResult<()> { pub fn add_loot(self, items: Vec<models::item::Item>) -> ActionResult<()> {
for item_desc in items.into_iter() { for item_desc in items.iter() {
let new_item = models::item::NewLoot::to_group(item_desc); let new_item = models::item::NewLoot::to_group(item_desc);
diesel::insert_into(schema::looted::table) diesel::insert_into(schema::looted::table)
.values(&new_item) .values(&new_item)

View File

@@ -12,7 +12,7 @@ type OwnedBy = Select<OwnedLoot, ItemColumns>;
/// It is also used as a public representation of Loot, since owner /// It is also used as a public representation of Loot, since owner
/// information is implicit. /// information is implicit.
/// Or maybe this is a little too confusing ?? /// Or maybe this is a little too confusing ??
#[derive(Debug, Queryable, Serialize)] #[derive(Debug, Queryable, Serialize, Deserialize, Clone)]
pub struct Item { pub struct Item {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
@@ -54,9 +54,6 @@ impl Loot {
} }
} }
/// Description of an item : (name, value in gold)
pub type ItemDesc<'a> = (&'a str, i32);
/// An item being looted or bought. /// An item being looted or bought.
/// ///
/// The owner is set to 0 in case of looting, /// The owner is set to 0 in case of looting,
@@ -71,19 +68,19 @@ pub(crate) struct NewLoot<'a> {
impl<'a> NewLoot<'a> { impl<'a> NewLoot<'a> {
/// A new loot going to the group (loot procedure) /// A new loot going to the group (loot procedure)
pub(crate) fn to_group(desc: ItemDesc<'a>) -> Self { pub(crate) fn to_group(desc: &'a Item) -> Self {
Self { Self {
name: desc.0, name: &desc.name,
base_price: desc.1, base_price: desc.base_price,
owner_id: 0, owner_id: 0,
} }
} }
/// A new loot going to a specific player (buy procedure) /// A new loot going to a specific player (buy procedure)
pub(crate) fn to_player(player: i32, desc: ItemDesc<'a>) -> Self { pub(crate) fn to_player(player: i32, desc: &'a Item) -> Self {
Self { Self {
name: desc.0, name: &desc.name,
base_price: desc.1, base_price: desc.base_price,
owner_id: player, owner_id: player,
} }
} }

View File

@@ -3,6 +3,6 @@ pub(super) mod item;
pub(super) mod player; pub(super) mod player;
pub use claim::Claim; pub use claim::Claim;
pub use item::Item; pub use item::{Item};
pub(crate) use item::Loot; pub(crate) use item::Loot;
pub use player::{Player, Wealth}; pub use player::{Player, Wealth};

View File

@@ -56,13 +56,11 @@
</nav> </nav>
<main class="section"> <main class="section">
<template v-if="isAdding"> <template v-if="isAdding">
<div v-if="playerIsGroup" class="box"> <Loot v-if="playerIsGroup"
<ItemInput v-if="playerIsGroup" :inventory="state.inventory"
:source="state.inventory"
@addItem="item => pending_loot.push(item)" @addItem="item => pending_loot.push(item)"
></ItemInput> @confirmAction="addNewLoot"
<button>Envoyer</button> ></Loot>
</div>
<AddingChest <AddingChest
:items="playerIsGroup ? pending_loot : state.inventory" :items="playerIsGroup ? pending_loot : state.inventory"
:perms="playerIsGroup ? {} : { canBuy: true }" :perms="playerIsGroup ? {} : { canBuy: true }"
@@ -89,8 +87,8 @@ import PlayerView from './components/PlayerView.js'
import HeaderBar from './components/HeaderBar.vue' import HeaderBar from './components/HeaderBar.vue'
import Wealth from './components/Wealth.vue' import Wealth from './components/Wealth.vue'
import Chest from './components/Chest.vue' import Chest from './components/Chest.vue'
import ItemInput from './components/ItemInput.vue' import Loot from './components/Loot.vue'
import { AppStorage } from './AppStorage' import { Api, AppStorage } from './AppStorage'
function getCookie(cname) { function getCookie(cname) {
var name = cname + "="; var name = cname + "=";
@@ -124,7 +122,7 @@ export default {
'AddingChest': Chest, // Alias to prevent component re-use 'AddingChest': Chest, // Alias to prevent component re-use
Chest, Chest,
Wealth, Wealth,
ItemInput, Loot,
}, },
created () { created () {
// Initiate with active player set to value found in cookie // Initiate with active player set to value found in cookie
@@ -149,7 +147,16 @@ export default {
} }
this.activeView = viewId; this.activeView = viewId;
}, },
switchPlayerChestVisibility () { AppStorage.switchPlayerChestVisibility(); }, switchPlayerChestVisibility () {
AppStorage.switchPlayerChestVisibility();
},
addNewLoot () {
Api.newLoot(this.pending_loot)
.then(_ => {
this.pending_loot = []
this.switchView('group');
});
}
}, },
computed: { computed: {
showPlayerChest () { return this.activeView == 'player' }, showPlayerChest () { return this.activeView == 'player' },

View File

@@ -52,7 +52,9 @@ export const Api = {
sellItems (player_id, items) { sellItems (player_id, items) {
const payload = { player_id, items }; const payload = { player_id, items };
return this.__doFetch("players/sell", 'POST', payload); return this.__doFetch("players/sell", 'POST', payload);
},
newLoot (items) {
return this.__doFetch("admin/add-loot", 'POST', items);
}, },
}; };
@@ -165,6 +167,9 @@ export const AppStorage = {
if (this.debug) console.log("cancel a non-existent request") if (this.debug) console.log("cancel a non-existent request")
} }
}); });
} },
addNewLoot (items) {
return Api.newLoot(items);
},
} }

View File

@@ -1,16 +1,18 @@
<template> <template>
<div class="container is-paddingless"> <div class="field is-horizontal">
<p class="heading">Ajouter un objet</p> <div class="field-label">
<label class="label">Nouvel objet</label>
</div>
<div class="field-body">
<div class="field"> <div class="field">
<label for="item_name" class="label">Nom</label> <div class="control is-expanded">
<div class="control is-expanded" :class="{'is-loading': is_loading }">
<input type="text" <input type="text"
id="item_name" placeholder="Nom de l'objet"
v-model="item.name" v-model="item.name"
@input="autoCompletion" @input="autoCompletion"
class="input" class="input"
autocomplete="on" autocomplete="on"
></input> >
</div> </div>
<div class="dropdown" :class="{'is-active': showCompletionFrame}"> <div class="dropdown" :class="{'is-active': showCompletionFrame}">
<div class="dropdown-menu"> <div class="dropdown-menu">
@@ -24,21 +26,26 @@
</div> </div>
</div> </div>
</div> </div>
<div class="field"> <div class="field is-expanded has-addons" v-show="item.name != ''">
<div class="control"> <p class="control"><a class="button is-static">PO</a></p>
<p class="control">
<input type="text" <input type="text"
placeholder="Prix"
class="input" class="input"
:class="{'is-danger': item.base_price == ''}" :class="{'is-danger': item.base_price == ''}"
v-model.number="item.base_price" v-model.number="item.base_price"
></input> >
</p>
</div> </div>
<div class="field">
<div class="control"> <div class="control">
<button class="button is-primary" <button class="button is-primary"
@click="addItem" @click="addItem"
>+</button> :disabled="!isItemValid"
>Ajouter</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
@@ -49,6 +56,7 @@
return { return {
is_loading: false, is_loading: false,
item: { item: {
id: 0,
name: '', name: '',
base_price: '', base_price: '',
}, },
@@ -59,7 +67,6 @@
autoCompletion () { autoCompletion () {
// Unset any previous value on input (for every field except item's name) // Unset any previous value on input (for every field except item's name)
this.item.base_price = ''; this.item.base_price = '';
if (this.item.name == '') { if (this.item.name == '') {
this.results = []; this.results = [];
} else { } else {
@@ -69,13 +76,13 @@
} }
}, },
setResult(result) { setResult(result) {
this.item.id = result.id;
this.item.name = result.name; this.item.name = result.name;
this.item.base_price = result.base_price; this.item.base_price = result.base_price;
// Clear results to close completionFrame // Clear results to close completionFrame
this.results = []; this.results = [];
}, },
addItem () { addItem () {
// TODO: check item is valid
this.$emit("addItem", this.item); this.$emit("addItem", this.item);
this.item = { this.item = {
name: '', name: '',
@@ -86,11 +93,15 @@
}, },
computed: { computed: {
showCompletionFrame () { return this.results.length > 0 }, showCompletionFrame () { return this.results.length > 0 },
isItemValid () { return this.item.name != '' && this.item.base_price != '' },
} }
} }
</script> </script>
<style scoped> <style scoped>
.dropdown, .dropdown-menu { min-width: 100%; margin-top: 0; padding-top: 0;} .dropdown, .dropdown-menu {
/*.dropdown { top: -0.75rem; }*/ min-width: 100%;
margin-top: 0;
padding-top: 0;
}
</style> </style>

View File

@@ -1,37 +1,33 @@
<template> <template>
<div> <div class="box">
<p class="heading has-text-left is-size-5"> <ItemInput
Nouveau loot - {{ looted.length }} objet(s)</p> @addItem="onAddItem"
<ItemInput @addItem="onAddItem" :source="inventory"></ItemInput> :source="inventory"
<p v-for="(item, idx) in looted" :key="idx" ></ItemInput>
class="has-text-left is-size-5"> <div class="field is-horizontal">
{{ item.name }} ({{ item.sell_value }}po) <div class="field-label"><label class="label">ou</label></div>
</p> <div class="field-body">
<div class="field">
<div class="control">
<button class="button is-primary">Depuis une liste</button>
</div>
</div>
</div>
</div>
<button class="button is-danger" @click="$emit('confirmAction')">Finaliser</button>
</div> </div>
</template> </template>
<script> <script>
import ItemInput from './ItemInput.vue' import ItemInput from './ItemInput.vue'
// List of items for autocomplete
const MOCK_ITEMS = [
{id: 35, name: "Cape d'invisibilité", sell_value: 30000},
{id: 8, name: "Arc long", sell_value: 10},
{id: 9, name: "Arc court", sell_value: 10},
];
export default { export default {
props: ["inventory"],
components: { ItemInput }, components: { ItemInput },
data () { return { data () { return {}; },
looted: [],
inventory: MOCK_ITEMS,
};
},
methods: { methods: {
onAddItem (item) { onAddItem (item) {
this.looted.push(item); this.$emit('addItem', item);
},
onClose () {
this.$emit('done');
}, },
} }

View File

@@ -3,6 +3,7 @@ use actix_files as fs;
use actix_web::{web, App, Error, HttpResponse, HttpServer}; use actix_web::{web, App, Error, HttpResponse, HttpServer};
use futures::Future; use futures::Future;
use lootalot_db::{DbApi, Pool, QueryResult}; use lootalot_db::{DbApi, Pool, QueryResult};
use lootalot_db::models::Item;
use std::env; use std::env;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@@ -162,6 +163,17 @@ pub(crate) fn serve() -> std::io::Result<()> {
db_call(pool, move |api| api.as_admin().resolve_claims()) db_call(pool, move |api| api.as_admin().resolve_claims())
}), }),
) )
.route(
"/add-loot",
web::post().to_async(
move |pool: AppPool, data: web::Json<Vec<Item>>| {
db_call(pool, move |api| api
.as_admin()
.add_loot(data.to_vec()),
)
}
)
)
.route( .route(
"/add-player", "/add-player",
web::get().to_async( web::get().to_async(