stash
This commit is contained in:
parent
ebd0db4b43
commit
2bb6624d50
4 changed files with 156 additions and 216 deletions
|
|
@ -2,12 +2,26 @@
|
|||
<BaseLayout>
|
||||
<main class="content">
|
||||
<div class="container-fluid p-0">
|
||||
<h1 class="h3 mb-3">Inventory Own & Friends"</h1>
|
||||
<h1 class="h3 mb-3">Inventory</h1>
|
||||
<div class="row mb-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="ownerSelect" class="form-label">Viewing inventory for</label>
|
||||
<select id="ownerSelect" class="form-select" v-model="selectedOwner">
|
||||
<option :value="user">{{ user }} (you)</option>
|
||||
<option v-for="group in ownerGroups" :value="group.handle" :key="group.handle">
|
||||
{{ group.handle }}
|
||||
</option>
|
||||
<option v-for="friend in friends" :value="friend.handle" :key="friend.handle">
|
||||
{{ friend.handle }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{{ user }}'s Inventory</h5>
|
||||
<h5 class="card-title">{{ selectedOwner }}'s Inventory</h5>
|
||||
<button v-if="layout === 'grid'" @click="layout = 'table'" class="btn">
|
||||
<b-icon-list></b-icon-list>
|
||||
</button>
|
||||
|
|
@ -25,7 +39,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in inventory_items" :key="item.id">
|
||||
<tr v-for="item in items" :key="item.id">
|
||||
<td>
|
||||
<router-link :to="itemRoute(item)">{{ item.name }}</router-link>
|
||||
</td>
|
||||
|
|
@ -34,10 +48,10 @@
|
|||
</td>
|
||||
<td class="d-none d-md-table-cell">{{ item.owned_quantity }}</td>
|
||||
<td class="table-action">
|
||||
<router-link :to="`${itemRoute(item)}/edit`">
|
||||
<router-link v-if="canEdit" :to="`${itemRoute(item)}/edit`">
|
||||
<b-icon-pencil-square></b-icon-pencil-square>
|
||||
</router-link>
|
||||
<a :href="`${itemRoute(item)}/delete`" @click.prevent="deleteInventoryItem(item)">
|
||||
<a v-if="canEdit" :href="`${itemRoute(item)}/delete`" @click.prevent="tryDeleteItem(item)">
|
||||
<b-icon-trash></b-icon-trash>
|
||||
</a>
|
||||
<router-link v-if="shortIdLink(item)" :to="shortIdLink(item)">
|
||||
|
|
@ -52,7 +66,7 @@
|
|||
</table>
|
||||
<div class="card-body" v-else>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 col-lg-3 col-xl-2" v-for="item in inventory_items"
|
||||
<div class="col-12 col-md-4 col-lg-3 col-xl-2" v-for="item in items"
|
||||
:key="item.id">
|
||||
<div class="card">
|
||||
<img :src="item.image" class="" :alt="item.image">
|
||||
|
|
@ -70,10 +84,10 @@
|
|||
<span class="float-right">{{ item.owned_quantity }}</span>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-danger btn-sm"
|
||||
@click="deleteInventoryItem(item)">Delete
|
||||
<button v-if="canEdit" class="btn btn-danger btn-sm"
|
||||
@click="tryDeleteItem(item)">Delete
|
||||
</button>
|
||||
<router-link :to="`${itemRoute(item)}/edit`"
|
||||
<router-link v-if="canEdit" :to="`${itemRoute(item)}/edit`"
|
||||
class="btn btn-primary btn-sm">Edit
|
||||
</router-link>
|
||||
<router-link v-if="shortIdLink(item)" :to="shortIdLink(item)"
|
||||
|
|
@ -93,8 +107,8 @@
|
|||
|
||||
</div>
|
||||
<div class="card">
|
||||
<button class="btn" @click="fetchInventoryItems">Refresh</button>
|
||||
<router-link to="/inventory/new" class="btn btn-primary">Add</router-link>
|
||||
<button class="btn" @click="fetchItemsForOwner">Refresh</button>
|
||||
<router-link v-if="canEdit" :to="addItemRoute" class="btn btn-primary">Add</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -115,6 +129,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
layout: "grid",
|
||||
selectedOwner: null,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -123,15 +138,53 @@ export default {
|
|||
...BIcons
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["inventory_items", "loaded_items", "identityIdByHandle", "groupIdByHandle"]),
|
||||
...mapState(["user", "storage_locations"]),
|
||||
...mapGetters(["inventory_items", "groupInventoryItems", "loaded_items", "identityIdByHandle", "groupIdByHandle"]),
|
||||
...mapState(["user", "storage_locations", "groups", "groupMemberships", "friends"]),
|
||||
// Groups hosted here plus groups only known via a GroupMembership pointer - see
|
||||
// Groups.vue's allGroups and InventoryNew.vue's ownerGroups for the same merge/dedupe.
|
||||
ownerGroups() {
|
||||
const hostedHandles = new Set(this.groups.map(group => group.handle))
|
||||
const foreign = this.groupMemberships.filter(m => !hostedHandles.has(m.handle))
|
||||
return [...this.groups, ...foreign].sort((a, b) => a.handle.localeCompare(b.handle))
|
||||
},
|
||||
isGroupSelected() {
|
||||
return !!this.selectedOwner && this.selectedOwner.startsWith('#')
|
||||
},
|
||||
// Own items and group items can be created/edited/deleted here; a friend's items are
|
||||
// shown for browsing only - the backend rejects writes for anyone but the owner or a
|
||||
// fellow group member (see inventory.py's perform_create/update/destroy).
|
||||
canEdit() {
|
||||
return this.selectedOwner === this.user || this.isGroupSelected
|
||||
},
|
||||
items() {
|
||||
// item_map is keyed by owner handle regardless of whether that owner is a group or a
|
||||
// friend, so the same getter serves both - see store.js's groupInventoryItems.
|
||||
return this.selectedOwner === this.user ? this.inventory_items : this.groupInventoryItems(this.selectedOwner)
|
||||
},
|
||||
addItemRoute() {
|
||||
return this.selectedOwner === this.user
|
||||
? '/inventory/new'
|
||||
: `/inventory/new?group=${encodeHandleForUrl(this.selectedOwner)}`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["fetchInventoryItems", "deleteInventoryItem", "fetchStorageLocations", "fetchIdMap"]),
|
||||
// Always the viewer's own items (fetchInventoryItems has no group filter); the owner
|
||||
// handle is always included so /inventory/:handle/:id has exactly one shape.
|
||||
...mapActions(["fetchInventoryItems", "fetchGroupInventoryItems", "fetchFriendInventoryItems",
|
||||
"deleteInventoryItem", "fetchStorageLocations", "fetchIdMap", "fetchGroups",
|
||||
"fetchGroupMemberships", "fetchFriends"]),
|
||||
fetchItemsForOwner() {
|
||||
if (this.selectedOwner === this.user) return this.fetchInventoryItems()
|
||||
if (this.isGroupSelected) return this.fetchGroupInventoryItems({groupHandle: this.selectedOwner})
|
||||
return this.fetchFriendInventoryItems({friendHandle: this.selectedOwner})
|
||||
},
|
||||
tryDeleteItem(item) {
|
||||
this.deleteInventoryItem(item).then(() => {
|
||||
this.fetchItemsForOwner()
|
||||
})
|
||||
},
|
||||
// The owner handle is derived from the item itself, not the current selection, so a
|
||||
// link stays correct even if the dropdown selection changes underneath it.
|
||||
itemRoute(item) {
|
||||
return `/inventory/${encodeHandleForUrl(this.user)}/${item.id}`
|
||||
return `/inventory/${encodeHandleForUrl(item.owner_group || item.owner)}/${item.id}`
|
||||
},
|
||||
only_images(files) {
|
||||
return files.filter(file => file.mime_type.startsWith("image/"));
|
||||
|
|
@ -156,10 +209,22 @@ export default {
|
|||
return {path: '/print', query: {kind: 'item', userHandle, item: item.id}}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedOwner() {
|
||||
this.fetchItemsForOwner()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Set before the first render so addItemRoute/items never see selectedOwner=null
|
||||
// while user is already populated (which would misroute to the group branch).
|
||||
this.selectedOwner = this.user
|
||||
},
|
||||
async mounted() {
|
||||
await this.fetchInventoryItems()
|
||||
await this.fetchStorageLocations()
|
||||
await this.fetchIdMap()
|
||||
await this.fetchGroups()
|
||||
await this.fetchGroupMemberships()
|
||||
await this.fetchFriends()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue