toolshed/frontend/src/views/StorageLocationNew.vue
2026-08-26 22:18:02 +02:00

143 lines
6.5 KiB
Vue

<template>
<BaseLayout>
<main class="content">
<div class="row">
<div class="col">
<div class="card">
<div class="card-header">Create New Storage Location</div>
<div class="card-body">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name"
placeholder="Enter storage location name" v-model="location.name">
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description"
placeholder="Enter description" v-model="location.description"></textarea>
</div>
<div class="mb-3">
<label for="category" class="form-label">Category</label>
<select class="form-select" id="category" name="category"
v-model="location.category">
<option value="">No Category</option>
<option v-for="category in categories" :value="category">
{{ category }}
</option>
</select>
</div>
<div class="mb-3">
<label for="owner" class="form-label">Owner</label>
<select class="form-select" id="owner" name="owner" v-model="location.owner_group">
<option :value="null">Myself</option>
<option v-for="group in ownerGroups" :value="group.handle" :key="group.handle">
{{ group.handle }}
</option>
</select>
</div>
<div class="mb-3">
<label for="parent" class="form-label">Parent Location</label>
<select class="form-select" id="parent" name="parent"
v-model="location.parent">
<option value="">No Parent</option>
<option v-for="parent in availableParents" :value="parent.id">
{{ parent.path }}
</option>
</select>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary" style="width: 100%"
@click="submitForm()">Add
</button>
</div>
</div>
</div>
</div>
</div>
</main>
</BaseLayout>
</template>
<script>
import * as BIcons from "bootstrap-icons-vue";
import {mapActions, mapState} from "vuex";
import BaseLayout from "@/components/BaseLayout.vue";
import {decodeHandleFromUrl, ownerOverviewRoute} from "@/router";
export default {
name: "StorageLocationNew",
components: {
BaseLayout,
...BIcons
},
data() {
return {
location: {
name: "",
description: "",
category: null,
parent: null,
// The group's own "#name@domain" handle once picked, or null for a personal
// location - createStorageLocation resolves the target domain from it directly.
owner_group: null
},
// The selected owner's own location list, for the parent dropdown - own personal
// locations, or the chosen group's own (see loadParentOptionsForOwner).
groupLocations: []
}
},
methods: {
ownerOverviewRoute,
...mapActions(['createStorageLocation', 'fetchInfo', 'fetchStorageLocations', 'fetchGroups',
'fetchGroupMemberships', 'fetchGroupStorageLocations']),
async loadParentOptionsForOwner() {
if (!this.location.owner_group) {
this.groupLocations = []
return
}
this.groupLocations = await this.fetchGroupStorageLocations({groupHandle: this.location.owner_group})
},
submitForm() {
// Convert empty strings to null for ForeignKey fields
const locationData = {
...this.location,
category: this.location.category === "" ? null : this.location.category,
parent: this.location.parent === "" ? null : this.location.parent
};
this.createStorageLocation(locationData).then(created => this.$router.push(ownerOverviewRoute(created)));
}
},
computed: {
...mapState(["categories", "storage_locations", "groups", "groupMemberships"]),
// Groups hosted here plus groups only known via a GroupMembership pointer (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))
},
availableParents() {
return this.location.owner_group ? this.groupLocations : this.storage_locations
}
},
watch: {
'location.owner_group'() {
// Previously-selected parent likely doesn't belong to the newly-chosen owner.
this.location.parent = null
this.loadParentOptionsForOwner()
}
},
async mounted() {
await this.fetchInfo();
await this.fetchStorageLocations();
await this.fetchGroups();
await this.fetchGroupMemberships();
if (this.$route.query.group) {
this.location.owner_group = decodeHandleFromUrl(this.$route.query.group)
}
}
}
</script>
<style scoped>
</style>