diff --git a/core/core/settings.py b/core/core/settings.py
index 2d4a818..6796112 100644
--- a/core/core/settings.py
+++ b/core/core/settings.py
@@ -15,9 +15,11 @@ import sys
import dotenv
from pathlib import Path
+
def truthy_str(s):
return s.lower() in ['true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly', 'sure', 'positive', 'uh-huh', '👍']
+
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
diff --git a/core/tickets/serializers.py b/core/tickets/serializers.py
index f5b7ef6..9f14e7e 100644
--- a/core/tickets/serializers.py
+++ b/core/tickets/serializers.py
@@ -55,8 +55,6 @@ class IssueSerializer(serializers.ModelSerializer):
ret = super().to_internal_value(data)
if 'state' in data:
ret['state'] = data['state']
-# if 'assigned_to' in data:
-# ret['assigned_to'] = data['assigned_to']
return ret
def validate(self, attrs):
diff --git a/web/src/components/Navbar.vue b/web/src/components/Navbar.vue
index eb7504b..ccfb0f0 100644
--- a/web/src/components/Navbar.vue
+++ b/web/src/components/Navbar.vue
@@ -49,12 +49,12 @@
@@ -65,19 +65,6 @@
-
-
-
{{ link.title }}
diff --git a/web/src/store.js b/web/src/store.js
index 3140a99..7430c8b 100644
--- a/web/src/store.js
+++ b/web/src/store.js
@@ -3,7 +3,7 @@ import router from './router';
import * as base64 from 'base-64';
import * as utf8 from 'utf8';
-import {ticketStateColorLookup, ticketStateIconLookup, http} from "@/utils";
+import {ticketStateColorLookup, ticketStateIconLookup, http, http_session} from "@/utils";
import sharedStatePlugin from "@/shared-state-plugin";
import persistentStatePlugin from "@/persistent-state-plugin";
@@ -11,16 +11,17 @@ const store = createStore({
state: {
keyIncrement: 0,
events: [],
- loadedItems: [],
- itemCache: {},
+ items: [],
loadedBoxes: [],
toasts: [],
- tickets: [],
users: [],
groups: [],
state_options: [],
shippingVouchers: [],
+ loadedItems: {},
+ loadedTickets: {},
+
lastEvent: 'all',
lastUsed: {},
searchQuery: '',
@@ -62,7 +63,14 @@ const store = createStore({
},
getters: {
route: state => router.currentRoute.value,
+ session: state => http_session(state.user.token),
getEventSlug: state => router.currentRoute.value.params.event ? router.currentRoute.value.params.event : state.lastEvent,
+ getAllItems: state => Object.values(state.loadedItems).flat(),
+ getAllTickets: state => Object.values(state.loadedTickets).flat(),
+ getEventItems: (state, getters) => getters.getEventSlug === 'all' ? getters.getAllItems : getters.getAllItems.filter(t => t.event === getters.getEventSlug || (t.event == null && getters.getEventSlug === 'none')),
+ getEventTickets: (state, getters) => getters.getEventSlug === 'all' ? getters.getAllTickets : getters.getAllTickets.filter(t => t.event === getters.getEventSlug || (t.event == null && getters.getEventSlug === 'none')),
+ isItemsLoaded: (state, getters) => (getters.getEventSlug === 'all' || getters.getEventSlug === 'none') ? !!state.loadedItems : Object.keys(state.loadedItems).includes(getters.getEventSlug),
+ isTicketsLoaded: (state, getters) => (getters.getEventSlug === 'all' || getters.getEventSlug === 'none') ? !!state.loadedTickets : Object.keys(state.loadedTickets).includes(getters.getEventSlug),
getActiveView: state => router.currentRoute.value.name || 'items',
getFilters: state => router.currentRoute.value.query,
getBoxes: state => state.loadedBoxes,
@@ -130,35 +138,48 @@ const store = createStore({
changeView(state, {view, slug}) {
router.push({path: `/${slug}/${view}`});
},
- replaceLoadedItems(state, newItems) {
- state.loadedItems = newItems;
- state.fetchedData = {...state.fetchedData, items: Date.now()}; // TODO: manage caching items for different events and search results correctly
- },
- setItemCache(state, {slug, items}) {
- state.itemCache[slug] = items;
- },
replaceBoxes(state, loadedBoxes) {
state.loadedBoxes = loadedBoxes;
state.fetchedData = {...state.fetchedData, boxes: Date.now()};
},
+ setItems(state, {slug, items}) {
+ state.loadedItems[slug] = items;
+ state.loadedItems = {...state.loadedItems};
+ console.log(state.loadedItems)
+ },
+ replaceItems(state, items) {
+ const groups = Object.groupBy(items, i => i.event ? i.event : 'none')
+ for (const [key, value] of Object.entries(groups)) state.loadedItems[key] = value;
+ state.loadedItems = {...state.loadedItems};
+ console.log(state.loadedItems)
+ },
updateItem(state, updatedItem) {
- const item = state.loadedItems.filter(({uid}) => uid === updatedItem.uid)[0];
+ const item = state.loadedItems[updatedItem.event?updatedItem.event:'none'].filter(
+ ({uid}) => uid === updatedItem.uid)[0];
Object.assign(item, updatedItem);
},
removeItem(state, item) {
- state.loadedItems = state.loadedItems.filter(it => it !== item);
+ state.loadedItems[item.event?item.event:'none'] = state.loadedItems[item.event].filter(it => it !== item);
},
appendItem(state, item) {
- state.loadedItems.push(item);
+ state.loadedItems[item.event?item.event:'none'].push(item);
+ },
+ setTickets(state, {slug, tickets}) {
+ state.loadedTickets[slug] = tickets;
+ state.loadedTickets = {...state.loadedTickets};
+ console.log(state.loadedTickets)
},
replaceTickets(state, tickets) {
- state.tickets = tickets;
- state.fetchedData = {...state.fetchedData, tickets: Date.now()};
+ const groups = Object.groupBy(tickets, t => t.event ? t.event : 'none')
+ for (const [key, value] of Object.entries(groups)) state.loadedTickets[key] = value;
+ state.loadedTickets = {...state.loadedTickets};
+ console.log(state.loadedTickets)
},
updateTicket(state, updatedTicket) {
- const ticket = state.tickets.filter(({id}) => id === updatedTicket.id)[0];
+ const ticket = state.loadedTickets[updatedTicket.event?updatedTicket.event:'none'].filter(
+ ({id}) => id === updatedTicket.id)[0];
Object.assign(ticket, updatedTicket);
- state.tickets = [...state.tickets];
+ state.loadedTickets = {...state.loadedTickets};
},
replaceUsers(state, users) {
state.users = users;
@@ -249,7 +270,7 @@ const store = createStore({
return false;
}
},
- async reloadToken({commit, state, getters}) {
+ async reloadToken({commit, state}) {
try {
if (state.user.username && state.user.password) {
const data = await fetch('/api/2/login/', {
@@ -297,59 +318,51 @@ const store = createStore({
async fetchImage({state}, url) {
return await fetch(url, {headers: {'Authorization': `Token ${state.user.token}`}});
},
- async loadUserInfo({commit, state}) {
- const {data, success} = await http.get('/2/self/', state.user.token);
+ async loadUserInfo({commit, getters}) {
+ const {data, success} = await getters.session.get('/2/self/');
commit('setPermissions', data.permissions);
},
- async loadEvents({commit, state}) {
+ async loadEvents({commit, state, getters}) {
if (!state.user.token) return;
if (state.fetchedData.events > Date.now() - 1000 * 60 * 60 * 24) return;
- const {data, success} = await http.get('/2/events/', state.user.token);
- if (data && success)
- commit('replaceEvents', data);
+ const {data, success} = await getters.session.get('/2/events/');
+ if (data && success) commit('replaceEvents', data);
},
- async createEvent({commit, dispatch, state}, event) {
- const {data, success} = await http.post('/2/events/', event, state.user.token);
- if (data && success)
- commit('replaceEvents', [...state.events, data]);
+ async createEvent({commit, dispatch, state, getters}, event) {
+ const {data, success} = await getters.session.post('/2/events/', event);
+ if (data && success) commit('replaceEvents', [...state.events, data]);
},
- async deleteEvent({commit, dispatch, state}, event_id) {
- const {data, success} = await http.delete(`/2/events/${event_id}/`, state.user.token);
+ async deleteEvent({commit, dispatch, state, getters}, event_id) {
+ const {data, success} = await getters.session.delete(`/2/events/${event_id}/`);
if (success) {
await dispatch('loadEvents')
commit('replaceEvents', [...state.events.filter(e => e.eid !== event_id)])
}
},
- async fetchTicketStates({commit, state}) {
+ async fetchTicketStates({commit, state, getters}) {
if (!state.user.token) return;
if (state.fetchedData.states > Date.now() - 1000 * 60 * 60 * 24) return;
- const {data, success} = await http.get('/2/tickets/states/', state.user.token);
- if (data && success)
- commit('replaceTicketStates', data);
+ const {data, success} = await getters.session.get('/2/tickets/states/');
+ if (data && success) commit('replaceTicketStates', data);
},
- changeEvent({dispatch, getters, commit}, eventName) {
- router.push({path: `/${eventName.slug}/${getters.getActiveView}/`});
- dispatch('loadEventItems');
+ async changeEvent({dispatch, getters, commit}, eventName) {
+ await router.push({path: `/${eventName.slug}/${getters.getActiveView}/`});
+ //dispatch('loadEventItems');
},
- changeView({getters}, link) {
- router.push({path: `/${getters.getEventSlug}/${link.path}/`});
+ async changeView({getters}, link) {
+ await router.push({path: `/${getters.getEventSlug}/${link.path}/`});
},
- showBoxContent({getters}, box) {
- router.push({path: `/${getters.getEventSlug}/items/`, query: {box}});
+ async showBoxContent({getters}, box) {
+ await router.push({path: `/${getters.getEventSlug}/items/`, query: {box}});
},
async loadEventItems({commit, getters, state}) {
if (!state.user.token) return;
if (state.fetchedData.items > Date.now() - 1000 * 60 * 60 * 24) return;
try {
- commit('replaceLoadedItems', []);
const slug = getters.getEventSlug;
- if (slug in state.itemCache) {
- commit('replaceLoadedItems', state.itemCache[slug]);
- }
- const {data, success} = await http.get(`/2/${slug}/items/`, state.user.token);
+ const {data, success} = await getters.session.get(`/2/${slug}/items/`);
if (data && success) {
- commit('replaceLoadedItems', data);
- commit('setItemCache', {slug, items: data});
+ commit('setItems', {slug, items: data});
}
} catch (e) {
console.error("Error loading items");
@@ -357,131 +370,124 @@ const store = createStore({
},
async searchEventItems({commit, getters, state}, query) {
const encoded_query = base64.encode(utf8.encode(query));
-
- const {data, success} = await http.get(`/2/${getters.getEventSlug}/items/${encoded_query}/`,
- state.user.token);
- if (data && success)
- commit('replaceLoadedItems', data);
+ const slug = getters.getEventSlug;
+ const {
+ data, success
+ } = await getters.session.get(`/2/${slug}/items/${encoded_query}/`);
+ if (data && success) {
+ commit('setItems', {slug, items: data});
+ }
},
- async loadBoxes({commit, state}) {
+ async loadBoxes({commit, state, getters}) {
if (!state.user.token) return;
if (state.fetchedData.boxes > Date.now() - 1000 * 60 * 60 * 24) return;
- const {data, success} = await http.get('/2/boxes/', state.user.token);
- if (data && success)
- commit('replaceBoxes', data);
+ const {data, success} = await getters.session.get('/2/boxes/');
+ if (data && success) commit('replaceBoxes', data);
},
- async createBox({commit, dispatch, state}, box) {
- const {data, success} = await http.post('/2/boxes/', box, state.user.token);
+ async createBox({commit, dispatch, state, getters}, box) {
+ const {data, success} = await getters.session.post('/2/boxes/', box);
commit('replaceBoxes', data);
dispatch('loadBoxes').then(() => {
commit('closeAddBoxModal');
});
},
- async deleteBox({commit, dispatch, state}, box_id) {
- await http.delete(`/2/boxes/${box_id}/`, state.user.token);
+ async deleteBox({commit, dispatch, state, getters}, box_id) {
+ await getters.session.delete(`/2/boxes/${box_id}/`);
dispatch('loadBoxes');
},
async updateItem({commit, getters, state}, item) {
const {
- data,
- success
- } = await http.put(`/2/${getters.getEventSlug}/item/${item.uid}/`, item, state.user.token);
+ data, success
+ } = await getters.session.put(`/2/${getters.getEventSlug}/item/${item.uid}/`, item);
commit('updateItem', data);
},
async markItemReturned({commit, getters, state}, item) {
- await http.patch(`/2/${getters.getEventSlug}/item/${item.uid}/`, {returned: true}, state.user.token);
+ await getters.session.patch(`/2/${getters.getEventSlug}/item/${item.uid}/`, {returned: true},
+ state.user.token);
commit('removeItem', item);
},
async deleteItem({commit, getters, state}, item) {
- await http.delete(`/2/${getters.getEventSlug}/item/${item.uid}/`, item, state.user.token);
+ await getters.session.delete(`/2/${getters.getEventSlug}/item/${item.uid}/`, item);
commit('removeItem', item);
},
async postItem({commit, getters, state}, item) {
commit('updateLastUsed', {box: item.box, cid: item.cid});
- const {data, success} = await http.post(`/2/${getters.getEventSlug}/item/`, item, state.user.token);
+ const {data, success} = await getters.session.post(`/2/${getters.getEventSlug}/item/`, item);
commit('appendItem', data);
},
- async loadTickets({commit, state}) {
+ async loadTickets({commit, state, getters}) {
if (!state.user.token) return;
- if (state.fetchedData.tickets > Date.now() - 1000 * 60 * 60 * 24) return;
- const {data, success} = await http.get('/2/tickets/', state.user.token);
- if (data && success)
- commit('replaceTickets', data);
+ //if (state.fetchedData.tickets > Date.now() - 1000 * 60 * 60 * 24) return;
+ const {data, success} = await getters.session.get('/2/tickets/');
+ if (data && success) commit('replaceTickets', data);
},
async searchEventTickets({commit, getters, state}, query) {
const encoded_query = base64.encode(utf8.encode(query));
- const {data, success} = await http.get(`/2/${getters.getEventSlug}/tickets/${encoded_query}/`,
- state.user.token);
- if (data && success)
- commit('replaceTickets', data);
+ const {
+ data, success
+ } = await getters.session.get(`/2/${getters.getEventSlug}/tickets/${encoded_query}/`);
+ if (data && success) commit('replaceTickets', data);
},
- async sendMail({commit, dispatch, state}, {id, message}) {
- const {data, success} = await http.post(`/2/tickets/${id}/reply/`, {message}, state.user.token);
+ async sendMail({commit, dispatch, state, getters}, {id, message}) {
+ const {data, success} = await getters.session.post(`/2/tickets/${id}/reply/`, {message},
+ state.user.token);
if (data && success) {
state.fetchedData.tickets = 0;
await dispatch('loadTickets');
}
},
- async postManualTicket({commit, dispatch, state}, {sender, message, title,}) {
- const {data, success} = await http.post(`/2/tickets/manual/`, {
- name: title,
- sender,
- body: message,
- recipient: 'mail@c3lf.de'
- }, state.user.token);
+ async postManualTicket({commit, dispatch, state, getters}, {sender, message, title,}) {
+ const {data, success} = await getters.session.post(`/2/tickets/manual/`, {
+ name: title, sender, body: message, recipient: 'mail@c3lf.de'
+ });
await dispatch('loadTickets');
},
- async postComment({commit, dispatch, state}, {id, message}) {
- const {data, success} = await http.post(`/2/tickets/${id}/comment/`, {comment: message}, state.user.token);
+ async postComment({commit, dispatch, state, getters}, {id, message}) {
+ const {data, success} = await getters.session.post(`/2/tickets/${id}/comment/`, {comment: message});
if (data && success) {
state.fetchedData.tickets = 0;
await dispatch('loadTickets');
}
},
- async loadUsers({commit, state}) {
+ async loadUsers({commit, state, getters}) {
if (!state.user.token) return;
if (state.fetchedData.users > Date.now() - 1000 * 60 * 60 * 24) return;
- const {data, success} = await http.get('/2/users/', state.user.token);
- if (data && success)
- commit('replaceUsers', data);
+ const {data, success} = await getters.session.get('/2/users/');
+ if (data && success) commit('replaceUsers', data);
},
- async loadGroups({commit, state}) {
+ async loadGroups({commit, state, getters}) {
if (!state.user.token) return;
if (state.fetchedData.groups > Date.now() - 1000 * 60 * 60 * 24) return;
- const {data, success} = await http.get('/2/groups/', state.user.token);
- if (data && success)
- commit('replaceGroups', data);
+ const {data, success} = await getters.session.get('/2/groups/');
+ if (data && success) commit('replaceGroups', data);
},
- async updateTicket({commit, state}, ticket) {
- const {data, success} = await http.put(`/2/tickets/${ticket.id}/`, ticket, state.user.token);
+ async updateTicket({commit, state, getters}, ticket) {
+ const {data, success} = await getters.session.put(`/2/tickets/${ticket.id}/`, ticket);
commit('updateTicket', data);
},
- async updateTicketPartial({commit, state}, {id, ...ticket}) {
- const {data, success} = await http.patch(`/2/tickets/${id}/`, ticket, state.user.token);
+ async updateTicketPartial({commit, state, getters}, {id, ...ticket}) {
+ const {data, success} = await getters.session.patch(`/2/tickets/${id}/`, ticket);
commit('updateTicket', data);
},
- async fetchShippingVouchers({commit, state}) {
+ async fetchShippingVouchers({commit, state, getters}) {
if (!state.user.token) return;
if (state.fetchedData.shippingVouchers > Date.now() - 1000 * 60 * 60 * 24) return;
- const {data, success} = await http.get('/2/shipping_vouchers/', state.user.token);
+ const {data, success} = await getters.session.get('/2/shipping_vouchers/');
if (data && success) {
commit('setShippingVouchers', data);
}
},
- async createShippingVoucher({dispatch, state}, code) {
- const {data, success} = await http.post('/2/shipping_vouchers/', code, state.user.token);
+ async createShippingVoucher({dispatch, state, getters}, code) {
+ const {data, success} = await getters.session.post('/2/shipping_vouchers/', code);
if (data && success) {
state.fetchedData.shippingVouchers = 0;
dispatch('fetchShippingVouchers');
}
},
- async claimShippingVoucher({dispatch, state}, {ticket, shipping_voucher_type}) {
+ async claimShippingVoucher({dispatch, state, getters}, {ticket, shipping_voucher_type}) {
const id = state.shippingVouchers.filter(voucher => voucher.type === shipping_voucher_type && voucher.issue_thread === null)[0].id;
- const {
- data,
- success
- } = await http.patch(`/2/shipping_vouchers/${id}/`, {issue_thread: ticket}, state.user.token);
+ const {data, success} = await getters.session.patch(`/2/shipping_vouchers/${id}/`, {issue_thread: ticket});
if (data && success) {
state.fetchedData.shippingVouchers = 0;
state.fetchedData.tickets = 0;
@@ -489,58 +495,30 @@ const store = createStore({
}
}
},
- plugins: [
- persistentStatePlugin({ // TODO change remember to some kind of enable field
- prefix: "lf_",
- debug: false,
- isLoadedKey: "persistent_loaded",
- state: [
- "remember",
- "user",
- "events",
- "lastUsed",
- ]
- }),
- sharedStatePlugin({
- debug: false,
- isLoadedKey: "shared_loaded",
- clearingMutation: "logout",
- afterInit: "afterSharedInit",
- state: [
- "test",
- "state_options",
- "fetchedData",
- "tickets",
- "users",
- "groups",
- "loadedBoxes",
- "loadedItems",
- "shippingVouchers",
- ],
- watch: [
- "test",
- "state_options",
- "fetchedData",
- "tickets",
- "users",
- "groups",
- "loadedBoxes",
- "loadedItems",
- "shippingVouchers",
- ],
- mutations: [
- //"replaceTickets",
- ],
- }),
- ],
+ plugins: [persistentStatePlugin({ // TODO change remember to some kind of enable field
+ prefix: "lf_",
+ debug: false,
+ isLoadedKey: "persistent_loaded",
+ state: ["remember", "user", "events", "lastUsed",]
+ }), sharedStatePlugin({
+ debug: true,
+ isLoadedKey: "shared_loaded",
+ clearingMutation: "logout",
+ afterInit: "afterSharedInit",
+ state: ["test", "state_options", "fetchedData", "loadedItems", "users", "groups", "loadedBoxes", "loadedTickets", "shippingVouchers",],
+ watch: ["test", "state_options", "fetchedData", "loadedItems", "users", "groups", "loadedBoxes", "loadedTickets", "shippingVouchers",],
+ mutations: [//"replaceTickets",
+ ],
+ }),],
});
store.watch((state) => state.user, (user) => {
if (store.getters.isLoggedIn) {
- if (router.currentRoute.value.name === 'login' && router.currentRoute.value.query.redirect)
+ if (router.currentRoute.value.name === 'login' && router.currentRoute.value.query.redirect) {
router.push(router.currentRoute.value.query.redirect);
- else if (router.currentRoute.value.name === 'login')
+ } else if (router.currentRoute.value.name === 'login') {
router.push('/');
+ }
} else {
if (router.currentRoute.value.name !== 'login') {
router.push({
diff --git a/web/src/utils.js b/web/src/utils.js
index 5910521..ebc911a 100644
--- a/web/src/utils.js
+++ b/web/src/utils.js
@@ -100,4 +100,12 @@ const http = {
}
}
-export {ticketStateColorLookup, ticketStateIconLookup, http};
\ No newline at end of file
+const http_session = token => ({
+ get: async (url) => await http.get(url, token),
+ post: async (url, data) => await http.post(url, data, token),
+ put: async (url, data) => await http.put(url, data, token),
+ patch: async (url, data) => await http.patch(url, data, token),
+ delete: async (url) => await http.delete(url, token),
+});
+
+export {ticketStateColorLookup, ticketStateIconLookup, http, http_session};
\ No newline at end of file
diff --git a/web/src/views/Items.vue b/web/src/views/Items.vue
index 5abcf5d..2f9e5ed 100644
--- a/web/src/views/Items.vue
+++ b/web/src/views/Items.vue
@@ -1,5 +1,5 @@
-
+
@@ -18,7 +18,7 @@
@@ -44,7 +44,7 @@
ticket.id === id);
+ const id = parseInt(this.route.params.id)
+ const ret = this.getAllTickets.find(ticket => ticket.id === id);
return ret ? ret : {};
},
shippingEmail() {
@@ -124,14 +124,15 @@ export default {
},
},
mounted() {
- this.scheduleAfterInit(() => [Promise.all([this.fetchTicketStates(), this.loadTickets(), this.loadUsers(), this.fetchShippingVouchers()]).then(()=>{
- if (this.ticket.state == "pending_new"){
- this.selected_state = "pending_open";
- this.changeTicketStatus(this.ticket)
- };
- this.selected_state = this.ticket.state;
- this.selected_assignee = this.ticket.assigned_to
- })]);
+ this.scheduleAfterInit(() => [Promise.all([this.fetchTicketStates(), this.loadTickets(), this.loadUsers(), this.fetchShippingVouchers()]).then(() => {
+ if (this.ticket.state == "pending_new") {
+ this.selected_state = "pending_open";
+ this.changeTicketStatus(this.ticket)
+ }
+ ;
+ this.selected_state = this.ticket.state;
+ this.selected_assignee = this.ticket.assigned_to
+ })]);
}
};
diff --git a/web/src/views/Tickets.vue b/web/src/views/Tickets.vue
index 5e971a0..bc288f1 100644
--- a/web/src/views/Tickets.vue
+++ b/web/src/views/Tickets.vue
@@ -1,11 +1,12 @@
-
+
-
@@ -34,6 +36,7 @@
{{ item.name }} |
{{ item.last_activity }} |
{{ item.assigned_to }} |
+ {{ item.event }} |
|