stash
This commit is contained in:
parent
2bb6624d50
commit
7276750c66
15 changed files with 552 additions and 169 deletions
|
|
@ -3,31 +3,34 @@
|
|||
<main class="content">
|
||||
<div class="container-fluid p-0">
|
||||
<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">{{ selectedOwner }}'s Inventory</h5>
|
||||
<button v-if="layout === 'grid'" @click="layout = 'table'" class="btn">
|
||||
<b-icon-list></b-icon-list>
|
||||
</button>
|
||||
<button v-else @click="layout = 'grid'" class="btn">
|
||||
<b-icon-grid></b-icon-grid>
|
||||
</button>
|
||||
<div class="card-header d-flex justify-content-between align-items-center flex-wrap">
|
||||
<h5 class="card-title mb-0">{{ selectedOwner }}'s Inventory</h5>
|
||||
<div class="d-flex align-items-center">
|
||||
<!--label for="ownerSelect" class="visually-hidden">Viewing inventory for</label-->
|
||||
<select id="ownerSelect" class="form-select form-select-sm me-2" style="width:auto"
|
||||
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 class="btn-group">
|
||||
<button class="btn" @click="fetchItemsForOwner">Refresh</button>
|
||||
<router-link v-if="canEdit" :to="addItemRoute" class="btn btn-primary">Add</router-link>
|
||||
<button v-if="layout === 'grid'" @click="layout = 'table'" class="btn">
|
||||
<b-icon-list></b-icon-list>
|
||||
</button>
|
||||
<button v-else @click="layout = 'grid'" class="btn">
|
||||
<b-icon-grid></b-icon-grid>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-striped" v-if="layout === 'table'">
|
||||
<thead>
|
||||
|
|
@ -106,10 +109,6 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card">
|
||||
<button class="btn" @click="fetchItemsForOwner">Refresh</button>
|
||||
<router-link v-if="canEdit" :to="addItemRoute" class="btn btn-primary">Add</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -122,14 +121,20 @@ import {mapActions, mapGetters, mapMutations, mapState} from "vuex";
|
|||
import * as BIcons from "bootstrap-icons-vue";
|
||||
import BaseLayout from "@/components/BaseLayout.vue";
|
||||
import AuthenticatedImage from "../components/AuthenticatedImage.vue";
|
||||
import {shortenedRoute, encodeHandleForUrl} from "@/router";
|
||||
import {shortenedRoute, encodeHandleForUrl, decodeHandleFromUrl} from "@/router";
|
||||
|
||||
export default {
|
||||
name: "Inventory",
|
||||
props: {
|
||||
// Matches /inventory/:owner; absent means "me". See docs/implementation.md#owner-filtered-overview-routes-use-path-segments-not-query-strings.
|
||||
owner: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
layout: "grid",
|
||||
selectedOwner: null,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -140,31 +145,37 @@ export default {
|
|||
computed: {
|
||||
...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.
|
||||
// Groups hosted here plus GroupMembership-only ones - see Groups.vue's allGroups 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))
|
||||
},
|
||||
// See docs/implementation.md#owner-filtered-overview-routes-use-path-segments-not-query-strings.
|
||||
selectedOwner: {
|
||||
get() {
|
||||
return this.owner ? decodeHandleFromUrl(this.owner) : this.user
|
||||
},
|
||||
set(value) {
|
||||
const path = value === this.user ? '/inventory' : `/inventory/${encodeHandleForUrl(value)}`
|
||||
this.$router.replace(path)
|
||||
}
|
||||
},
|
||||
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).
|
||||
// Friend items are browse-only - inventory.py's perform_create/update/destroy reject writes from anyone but the owner/a fellow group member.
|
||||
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.
|
||||
// item_map is keyed by owner handle regardless of group vs friend - 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)}`
|
||||
: `/inventory/new/${encodeHandleForUrl(this.selectedOwner)}`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -181,8 +192,7 @@ export default {
|
|||
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.
|
||||
// Derived from the item's own owner, not the current selection, so the link stays correct if the dropdown changes underneath it.
|
||||
itemRoute(item) {
|
||||
return `/inventory/${encodeHandleForUrl(item.owner_group || item.owner)}/${item.id}`
|
||||
},
|
||||
|
|
@ -210,16 +220,12 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
selectedOwner() {
|
||||
owner() {
|
||||
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() {
|
||||
this.fetchItemsForOwner()
|
||||
await this.fetchStorageLocations()
|
||||
await this.fetchIdMap()
|
||||
await this.fetchGroups()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue