This commit is contained in:
j3d1 2024-01-31 22:51:01 +01:00
parent 4645a2f48c
commit be96901129
10 changed files with 272 additions and 111 deletions

View file

@ -52,11 +52,6 @@ export default {
}, },
created: function () { created: function () {
document.title = document.location.hostname; document.title = document.location.hostname;
this.loadEvents().then(() => {
/*if (this.isLoggedIn) {
this.afterLogin();
}*/
});
} }
}; };
</script> </script>

View file

@ -7,7 +7,7 @@
<script> <script>
import {mapActions} from "vuex"; import {mapActions, mapGetters, mapMutations} from "vuex";
export default { export default {
name: "AuthenticatedImage", name: "AuthenticatedImage",
@ -16,6 +16,7 @@ export default {
type: String, type: String,
required: true required: true
}, },
cached: Boolean
}, },
data() { data() {
return { return {
@ -23,8 +24,12 @@ export default {
servers: [] servers: []
} }
}, },
computed: {
...mapGetters(['getThumbnail']),
},
methods: { methods: {
...mapActions(['fetchImage']), ...mapActions(['fetchImage']),
...mapMutations(['setThumbnail']),
async loadImage() { async loadImage() {
const response = await this.fetchImage(this.src); const response = await this.fetchImage(this.src);
const mime_type = response.headers.get("content-type"); const mime_type = response.headers.get("content-type");
@ -32,10 +37,22 @@ export default {
const base64 = btoa(new Uint8Array(buf) const base64 = btoa(new Uint8Array(buf)
.reduce((data, byte) => data + String.fromCharCode(byte), "")); .reduce((data, byte) => data + String.fromCharCode(byte), ""));
this.image_data = "data:" + mime_type + ";base64," + base64; this.image_data = "data:" + mime_type + ";base64," + base64;
if (this.cached)
this.setThumbnail({
url: this.src,
data: this.image_data
});
} }
}, },
mounted() { mounted() {
setTimeout(() => { setTimeout(() => {
if (this.cached) {
const c = this.getThumbnail(this.src);
if (c) {
this.image_data = c;
return;
}
}
this.loadImage(); this.loadImage();
}, 0); }, 0);
} }

View file

@ -25,8 +25,8 @@
<ul> <ul>
<li v-for="attachment in item.attachments" @click="openLightboxModalWith(attachment)"> <li v-for="attachment in item.attachments" @click="openLightboxModalWith(attachment)">
<AuthenticatedImage :src="`/media/2/256/${attachment.hash}/`" :alt="attachment.name" <AuthenticatedImage :src="`/media/2/256/${attachment.hash}/`" :alt="attachment.name"
v-if="attachment.mime_type.startsWith('image/')"/> v-if="attachment.mime_type.startsWith('image/')" cached/>
<AuthenticatedDataLink :href="`/media/2/256/${attachment.hash}/`" :download="attachment.name" <AuthenticatedDataLink :href="`/media/2/${attachment.hash}/`" :download="attachment.name"
v-else/> v-else/>
</li> </li>
</ul> </ul>

View file

@ -25,7 +25,15 @@ export default {
computed: { computed: {
...mapState(['state_options']), ...mapState(['state_options']),
lookupState: function () { lookupState: function () {
try {
if (this.item.state)
return this.state_options.find(state => state.value === this.item.state); return this.state_options.find(state => state.value === this.item.state);
} catch (e) {
}
return {
text: 'Unknown',
value: 'unknown'
};
}, },
colorLookup: function () { colorLookup: function () {
if (this.item.state.startsWith('closed_')) { if (this.item.state.startsWith('closed_')) {

View file

@ -4,10 +4,71 @@ export default (config) => {
if (!('isLoadedKey' in config)) { if (!('isLoadedKey' in config)) {
throw new Error("isLoadedKey not defined in config"); throw new Error("isLoadedKey not defined in config");
} }
if (('asyncFetch' in config) && !('lastfetched' in config)) {
throw new Error("asyncFetch defined but lastfetched not defined in config");
}
if (config.debug) console.log('plugin created'); if (config.debug) console.log('plugin created');
/** may only be called from worker */ /** may only be called from worker */
const clone = (obj) => {
if (isProxy(obj)) {
obj = toRaw(obj);
}
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj.__proto__ === ({}).__proto__) {
return Object.assign({}, obj);
}
if (obj.__proto__ === [].__proto__) {
return obj.slice();
}
return obj;
}
const deepEqual = (a, b) => {
if (a === b) {
return true;
}
if (a === null || b === null) {
return false;
}
if (a.__proto__ === ({}).__proto__ && b.__proto__ === ({}).__proto__) {
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (let key in b) {
if (!(key in a)) {
return false;
}
}
for (let key in a) {
if (!(key in b)) {
return false;
}
if (!deepEqual(a[key], b[key])) {
return false;
}
}
return true;
}
if (a.__proto__ === [].__proto__ && b.__proto__ === [].__proto__) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!deepEqual(a[i], b[i])) {
return false;
}
}
return true;
}
return false;
}
const worker_fun = function (self, ctx) { const worker_fun = function (self, ctx) {
/* globals WebSocket, SharedWorker, onconnect, onmessage, postMessage, close, location */ /* globals WebSocket, SharedWorker, onconnect, onmessage, postMessage, close, location */
@ -105,15 +166,14 @@ export default (config) => {
case 'state_diff': case 'state_diff':
if (message_data.key in state) { if (message_data.key in state) {
if (!deepEqual(state[message_data.key], message_data.old_value)) { if (!deepEqual(state[message_data.key], message_data.old_value)) {
if (ctx.debug) console.log("state diff old value mismatch | state:", if (ctx.debug) console.log("state diff old value mismatch | state:", state[message_data.key], " old:", message_data.old_value);
state[message_data.key], " old:", message_data.old_value);
} }
if (!deepEqual(state[message_data.key], message_data.new_value)) { if (!deepEqual(state[message_data.key], message_data.new_value)) {
if (ctx.debug) console.log("state diff changed | state:", state[message_data.key], " new:", message_data.new_value);
state[message_data.key] = message_data.new_value; state[message_data.key] = message_data.new_value;
others(message_data); others(message_data);
} else { } else {
if (ctx.debug) console.log("state diff no change | state:", if (ctx.debug) console.log("state diff no change | state:", state[message_data.key], " new:", message_data.new_value);
state[message_data.key], " new:", message_data.new_value);
} }
} else { } else {
if (ctx.debug) console.log("state diff key not found", message_data.key); if (ctx.debug) console.log("state diff key not found", message_data.key);
@ -154,8 +214,9 @@ export default (config) => {
} }
const worker_context = { const worker_context = {
location: {...location}, location: {
debug: config.debug protocol: location.protocol, host: location.host
}, bug: config.debug
} }
const worker_code = '(' + worker_fun.toString() + ')(self,' + JSON.stringify(worker_context) + ')'; const worker_code = '(' + worker_fun.toString() + ')(self,' + JSON.stringify(worker_context) + ')';
const worker_url = 'data:application/javascript;base64,' + btoa(worker_code); const worker_url = 'data:application/javascript;base64,' + btoa(worker_code);
@ -203,19 +264,26 @@ export default (config) => {
} else { } else {
for (let key in e.data.state) { for (let key in e.data.state) {
if (key in store.state) { if (key in store.state) {
if (config.debug) console.log('worker state init received', key, e.data.state[key]); if (config.debug) console.log('worker state init received', key, clone(e.data.state[key]));
if (!deepEqual(store.state[key], e.data.state[key])) {
store.state[key] = e.data.state[key]; store.state[key] = e.data.state[key];
}
} else { } else {
if (config.debug) console.log("state init key not found", key); if (config.debug) console.log("state init key not found", key);
} }
} }
} }
store[config.isLoadedKey] = true; store.state[config.isLoadedKey] = true;
if ('afterInit' in config) {
setTimeout(() => {
store.dispatch(config.afterInit);
}, 0);
}
break; break;
case 'state_diff': case 'state_diff':
if (config.debug) console.log('state_diff', e.data); if (config.debug) console.log('state_diff', e.data);
if (e.data.key in store.state) { if (e.data.key in store.state) {
if (config.debug) console.log('worker state update', e.data.key, e.data.new_value); if (config.debug) console.log('worker state update', e.data.key, clone(e.data.new_value));
//TODO this triggers the watcher again, but we don't want that //TODO this triggers the watcher again, but we don't want that
store.state[e.data.key] = e.data.new_value; store.state[e.data.key] = e.data.new_value;
} else { } else {
@ -248,7 +316,7 @@ export default (config) => {
if ('state' in config) { if ('state' in config) {
config.watch.forEach((member) => { config.watch.forEach((member) => {
store.watch((state, getters) => state[member], (newValue, oldValue) => { store.watch((state, getters) => state[member], (newValue, oldValue) => {
if (config.debug) console.log('watch', member, newValue, oldValue); if (config.debug) console.log('watch', member, clone(newValue), clone(oldValue));
updateWorkerState(member, newValue, oldValue); updateWorkerState(member, newValue, oldValue);
}); });
}); });

View file

@ -31,9 +31,19 @@ const store = createStore({
expiry: null, expiry: null,
}, },
fetchedData: {}, thumbnailCache: {},
shared_loaded: false, fetchedData: {
events: 0,
items: 0,
boxes: 0,
tickets: 0,
users: 0,
groups: 0,
states: 0,
},
persistent_loaded: false, persistent_loaded: false,
shared_loaded: false,
afterInitHandlers: [],
showAddBoxModal: false, showAddBoxModal: false,
}, },
@ -75,6 +85,12 @@ const store = createStore({
isLoggedIn(state) { isLoggedIn(state) {
return state.user && state.user.username !== null && state.user.token !== null; return state.user && state.user.username !== null && state.user.token !== null;
}, },
getThumbnail: (state) => (url) => {
if (!url) return null;
if (!(url in state.thumbnailCache))
return null;
return state.thumbnailCache[url];
},
}, },
mutations: { mutations: {
updateLastEvent(state, slug) { updateLastEvent(state, slug) {
@ -82,21 +98,25 @@ const store = createStore({
}, },
replaceEvents(state, events) { replaceEvents(state, events) {
state.events = events; state.events = events;
state.fetchedData = {...state.fetchedData, events: Date.now()};
}, },
replaceTicketStates(state, states) { replaceTicketStates(state, states) {
state.state_options = states; state.state_options = states;
state.fetchedData = {...state.fetchedData, states: Date.now()};
}, },
changeView(state, {view, slug}) { changeView(state, {view, slug}) {
router.push({path: `/${slug}/${view}`}); router.push({path: `/${slug}/${view}`});
}, },
replaceLoadedItems(state, newItems) { replaceLoadedItems(state, newItems) {
state.loadedItems = 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}) { setItemCache(state, {slug, items}) {
state.itemCache[slug] = items; state.itemCache[slug] = items;
}, },
replaceBoxes(state, loadedBoxes) { replaceBoxes(state, loadedBoxes) {
state.loadedBoxes = loadedBoxes; state.loadedBoxes = loadedBoxes;
state.fetchedData = {...state.fetchedData, boxes: Date.now()};
}, },
updateItem(state, updatedItem) { updateItem(state, updatedItem) {
const item = state.loadedItems.filter(({uid}) => uid === updatedItem.uid)[0]; const item = state.loadedItems.filter(({uid}) => uid === updatedItem.uid)[0];
@ -110,12 +130,15 @@ const store = createStore({
}, },
replaceTickets(state, tickets) { replaceTickets(state, tickets) {
state.tickets = tickets; state.tickets = tickets;
state.fetchedData = {...state.fetchedData, tickets: Date.now()};
}, },
replaceUsers(state, users) { replaceUsers(state, users) {
state.users = users; state.users = users;
state.fetchedData = {...state.fetchedData, users: Date.now()};
}, },
replaceGroups(state, groups) { replaceGroups(state, groups) {
state.groups = groups; state.groups = groups;
state.fetchedData = {...state.fetchedData, groups: Date.now()};
}, },
updateTicket(state, updatedTicket) { updateTicket(state, updatedTicket) {
const ticket = state.tickets.filter(({id}) => id === updatedTicket.id)[0]; const ticket = state.tickets.filter(({id}) => id === updatedTicket.id)[0];
@ -169,6 +192,9 @@ const store = createStore({
setTest(state, test) { setTest(state, test) {
state.test = test; state.test = test;
}, },
setThumbnail(state, {url, data}) {
state.thumbnailCache[url] = data;
},
}, },
actions: { actions: {
async login({commit}, {username, password, remember}) { async login({commit}, {username, password, remember}) {
@ -213,31 +239,49 @@ const store = createStore({
store.commit('logout'); store.commit('logout');
}, },
//async verifyToken({commit, state}) { //async verifyToken({commit, state}) {
/*async afterLogin({dispatch, state}) { async afterLogin({dispatch, state}) {
//const boxes = dispatch('loadBoxes'); let promises = [];
//const states = dispatch('fetchTicketStates'); promises.push(dispatch('loadBoxes'));
//const items = dispatch('loadEventItems'); promises.push(dispatch('fetchTicketStates'));
//const tickets = dispatch('loadTickets'); promises.push(dispatch('loadEventItems'));
//const user = dispatch('loadUserInfo'); promises.push(dispatch('loadTickets'));
//await Promise.all([boxes, items, tickets, user, states]);
if (!state.user.permissions) { if (!state.user.permissions) {
const user = dispatch('loadUserInfo'); promises.push(dispatch('loadUserInfo'));
await Promise.all([user]);
} }
},*/ await Promise.all(promises);
},
async afterSharedInit({dispatch, state}) {
const handlers = state.afterInitHandlers;
state.afterInitHandlers = [];
await Promise.all(handlers.map(h => h()).flat());
},
scheduleAfterInit({dispatch, state}, handler) {
if (state.shared_loaded) {
Promise.all(handler()).then(() => {
});
} else {
state.afterInitHandlers.push(handler);
}
},
async fetchImage({state}, url) { async fetchImage({state}, url) {
return await fetch(url, {headers: {'Authorization': `Token ${state.user.token}`}}); return await fetch(url, {headers: {'Authorization': `Token ${state.user.token}`}});
}, },
async loadUserInfo({commit, state}) { async loadUserInfo({commit, state}) {
const {data} = await http.get('/2/self/', state.user.token); const {data, success} = await http.get('/2/self/', state.user.token);
commit('setPermissions', data.permissions); commit('setPermissions', data.permissions);
}, },
async loadEvents({commit, state}) { async loadEvents({commit, state}) {
const {data} = await http.get('/2/events/', state.user.token); 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); commit('replaceEvents', data);
}, },
async fetchTicketStates({commit, state}) { async fetchTicketStates({commit, state}) {
const {data} = await http.get('/2/tickets/states/', state.user.token); 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); commit('replaceTicketStates', data);
}, },
changeEvent({dispatch, getters, commit}, eventName) { changeEvent({dispatch, getters, commit}, eventName) {
@ -251,15 +295,19 @@ const store = createStore({
router.push({path: `/${getters.getEventSlug}/items/`, query: {box}}); router.push({path: `/${getters.getEventSlug}/items/`, query: {box}});
}, },
async loadEventItems({commit, getters, state}) { async loadEventItems({commit, getters, state}) {
if (!state.user.token) return;
if (state.fetchedData.items > Date.now() - 1000 * 60 * 60 * 24) return;
try { try {
commit('replaceLoadedItems', []); commit('replaceLoadedItems', []);
const slug = getters.getEventSlug; const slug = getters.getEventSlug;
if (slug in state.itemCache) { if (slug in state.itemCache) {
commit('replaceLoadedItems', state.itemCache[slug]); commit('replaceLoadedItems', state.itemCache[slug]);
} }
const {data} = await http.get(`/2/${slug}/items/`, state.user.token); const {data, success} = await http.get(`/2/${slug}/items/`, state.user.token);
if (data && success) {
commit('replaceLoadedItems', data); commit('replaceLoadedItems', data);
commit('setItemCache', {slug, items: data}); commit('setItemCache', {slug, items: data});
}
} catch (e) { } catch (e) {
console.error("Error loading items"); console.error("Error loading items");
} }
@ -268,15 +316,19 @@ const store = createStore({
const foo = utf8.encode(query); const foo = utf8.encode(query);
const bar = base64.encode(foo); const bar = base64.encode(foo);
const {data} = await http.get(`/2/${getters.getEventSlug}/items/${bar}/`, state.user.token); const {data, success} = await http.get(`/2/${getters.getEventSlug}/items/${bar}/`, state.user.token);
if (data && success)
commit('replaceLoadedItems', data); commit('replaceLoadedItems', data);
}, },
async loadBoxes({commit, state}) { async loadBoxes({commit, state}) {
const {data} = await http.get('/2/boxes/', state.user.token); 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); commit('replaceBoxes', data);
}, },
async createBox({commit, dispatch, state}, box) { async createBox({commit, dispatch, state}, box) {
const {data} = await http.post('/2/boxes/', box, state.user.token); const {data, success} = await http.post('/2/boxes/', box, state.user.token);
commit('replaceBoxes', data); commit('replaceBoxes', data);
dispatch('loadBoxes').then(() => { dispatch('loadBoxes').then(() => {
commit('closeAddBoxModal'); commit('closeAddBoxModal');
@ -287,7 +339,10 @@ const store = createStore({
dispatch('loadBoxes'); dispatch('loadBoxes');
}, },
async updateItem({commit, getters, state}, item) { async updateItem({commit, getters, state}, item) {
const {data} = await http.put(`/2/${getters.getEventSlug}/item/${item.uid}/`, item, state.user.token); const {
data,
success
} = await http.put(`/2/${getters.getEventSlug}/item/${item.uid}/`, item, state.user.token);
commit('updateItem', data); commit('updateItem', data);
}, },
async markItemReturned({commit, getters, state}, item) { async markItemReturned({commit, getters, state}, item) {
@ -300,19 +355,22 @@ const store = createStore({
}, },
async postItem({commit, getters, state}, item) { async postItem({commit, getters, state}, item) {
commit('updateLastUsed', {box: item.box, cid: item.cid}); commit('updateLastUsed', {box: item.box, cid: item.cid});
const {data} = await http.post(`/2/${getters.getEventSlug}/item/`, item, state.user.token); const {data, success} = await http.post(`/2/${getters.getEventSlug}/item/`, item, state.user.token);
commit('appendItem', data); commit('appendItem', data);
}, },
async loadTickets({commit, state}) { async loadTickets({commit, state}) {
const {data} = await http.get('/2/tickets/', state.user.token); 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); commit('replaceTickets', data);
}, },
async sendMail({commit, dispatch, state}, {id, message}) { async sendMail({commit, dispatch, state}, {id, message}) {
const {data} = await http.post(`/2/tickets/${id}/reply/`, {message}, state.user.token); const {data, success} = await http.post(`/2/tickets/${id}/reply/`, {message}, state.user.token);
await dispatch('loadTickets'); await dispatch('loadTickets');
}, },
async postManualTicket({commit, dispatch, state}, {sender, message, title,}) { async postManualTicket({commit, dispatch, state}, {sender, message, title,}) {
const {data} = await http.post(`/2/tickets/manual/`, { const {data, success} = await http.post(`/2/tickets/manual/`, {
name: title, name: title,
sender, sender,
body: message, body: message,
@ -320,53 +378,68 @@ const store = createStore({
}, state.user.token); }, state.user.token);
await dispatch('loadTickets'); await dispatch('loadTickets');
}, },
async postComment({commit, dispatch, staee}, {id, message}) { async postComment({commit, dispatch, state}, {id, message}) {
const {data} = await http.post(`/2/tickets/${id}/comment/`, {comment: message}, state.user.token); const {data, success} = await http.post(`/2/tickets/${id}/comment/`, {comment: message}, state.user.token);
await dispatch('loadTickets'); await dispatch('loadTickets');
}, },
async loadUsers({commit, state}) { async loadUsers({commit, state}) {
const {data} = await http.get('/2/users/', state.user.token); 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); commit('replaceUsers', data);
}, },
async loadGroups({commit, state}) { async loadGroups({commit, state}) {
const {data} = await http.get('/2/groups/', state.user.token); 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); commit('replaceGroups', data);
}, },
async updateTicket({commit, state}, ticket) { async updateTicket({commit, state}, ticket) {
const {data} = await http.put(`/2/tickets/${ticket.id}/`, ticket, state.user.token); const {data, success} = await http.put(`/2/tickets/${ticket.id}/`, ticket, state.user.token);
commit('updateTicket', data); commit('updateTicket', data);
}, },
async updateTicketPartial({commit, state}, {id, ...ticket}) { async updateTicketPartial({commit, state}, {id, ...ticket}) {
const {data} = await http.patch(`/2/tickets/${id}/`, ticket, state.user.token); const {data, success} = await http.patch(`/2/tickets/${id}/`, ticket, state.user.token);
commit('updateTicket', data); commit('updateTicket', data);
} }
}, },
plugins: [ plugins: [
persistentStatePlugin({ // TODO change remember to some kind of enable field persistentStatePlugin({ // TODO change remember to some kind of enable field
prefix: "lf_", prefix: "lf_",
debug: true, debug: false,
isLoadedKey: "persistent_loaded", isLoadedKey: "persistent_loaded",
state: [ state: [
"remember", "remember",
"user", "user",
"events",
] ]
}), }),
sharedStatePlugin({ sharedStatePlugin({
debug: true, debug: false,
isLoadedKey: "shared_loaded", isLoadedKey: "shared_loaded",
clearingMutation: "logout", clearingMutation: "logout",
afterInit: "afterSharedInit",
state: [ state: [
"test", "test",
"state_options", "state_options",
"events", "fetchedData",
"fetchData",
"tickets", "tickets",
"users",
"groups",
"loadedBoxes",
"loadedItems",
], ],
watch: [ watch: [
"test", "test",
"state_options", "state_options",
"events", "fetchedData",
"fetchData", "tickets",
"users",
"groups",
"loadedBoxes",
"loadedItems",
], ],
mutations: [ mutations: [
//"replaceTickets", //"replaceTickets",
@ -375,19 +448,6 @@ const store = createStore({
], ],
}); });
// watch: {
// user: function (user) {
// console.log('user changed', user);
// if (user.token) {
// }
// //if(token && this.state.route.name === 'login') {
// //if (state.route.name !== 'login')
// // router.push('/login');
// //if (state.route.name === 'login' && state.route.query.redirect)
// // router.push(state.route.query.redirect);
// },
// },
store.watch((state) => state.user, (user) => { store.watch((state) => state.user, (user) => {
console.log('user changed', user); console.log('user changed', user);
if (store.getters.isLoggedIn) { if (store.getters.isLoggedIn) {

View file

@ -25,6 +25,20 @@ function ticketStateIconLookup(ticket) {
} }
const http = { const http = {
get: async (url, token) => {
if (!token) {
return null;
}
const response = await fetch('/api' + url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": `Token ${token}`,
},
});
const success = response.status === 200 || response.status === 201;
return {data: await response.json() || {}, success};
},
post: async (url, data, token) => { post: async (url, data, token) => {
if (!token) { if (!token) {
return null; return null;
@ -37,19 +51,8 @@ const http = {
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}); });
return {data: await response.json()}; const success = response.status === 200 || response.status === 201;
}, get: async (url, token) => { return {data: await response.json() || {}, success};
if (!token) {
return null;
}
const response = await fetch('/api' + url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": `Token ${token}`,
},
});
return {data: await response.json()};
}, },
put: async (url, data, token) => { put: async (url, data, token) => {
if (!token) { if (!token) {
@ -63,7 +66,8 @@ const http = {
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}); });
return {data: await response.json()}; const success = response.status === 200 || response.status === 201;
return {data: await response.json() || {}, success};
}, },
patch: async (url, data, token) => { patch: async (url, data, token) => {
if (!token) { if (!token) {
@ -77,7 +81,8 @@ const http = {
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}); });
return {data: await response.json()}; const success = response.status === 200 || response.status === 201;
return {data: await response.json() || {}, success};
}, },
delete: async (url, token) => { delete: async (url, token) => {
if (!token) { if (!token) {
@ -90,7 +95,8 @@ const http = {
"Authorization": `Token ${token}`, "Authorization": `Token ${token}`,
}, },
}); });
return {data: await response.json()}; const success = response.status === 200 || response.status === 201;
return {data: await response.json() || {}, success};
} }
} }

View file

@ -23,13 +23,15 @@
> >
<template #actions="{ item }"> <template #actions="{ item }">
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-success" @click.stop="confirm('return Item?') && markItemReturned(item)" title="returned"> <button class="btn btn-success"
@click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
<font-awesome-icon icon="check"/> <font-awesome-icon icon="check"/>
</button> </button>
<button class="btn btn-secondary" @click.stop="openEditingModalWith(item)" title="edit"> <button class="btn btn-secondary" @click.stop="openEditingModalWith(item)" title="edit">
<font-awesome-icon icon="edit"/> <font-awesome-icon icon="edit"/>
</button> </button>
<button class="btn btn-danger" @click.stop="confirm('delete Item?') && deleteItem(item)" title="delete"> <button class="btn btn-danger" @click.stop="confirm('delete Item?') && deleteItem(item)"
title="delete">
<font-awesome-icon icon="trash"/> <font-awesome-icon icon="trash"/>
</button> </button>
</div> </div>
@ -45,7 +47,7 @@
v-slot="{ item }" v-slot="{ item }"
@itemActivated="openLightboxModalWith($event)" @itemActivated="openLightboxModalWith($event)"
> >
<AuthenticatedImage v-if="item.file" <AuthenticatedImage v-if="item.file" cached
:src="`/media/2/256/${item.file}/`" :src="`/media/2/256/${item.file}/`"
class="card-img-top img-fluid" class="card-img-top img-fluid"
/> />
@ -93,7 +95,7 @@ export default {
...mapGetters(['layout']), ...mapGetters(['layout']),
}, },
methods: { methods: {
...mapActions(['deleteItem', 'markItemReturned']), ...mapActions(['deleteItem', 'markItemReturned', 'loadEventItems', 'scheduleAfterInit']),
openLightboxModalWith(item) { openLightboxModalWith(item) {
this.lightboxHash = item.file; this.lightboxHash = item.file;
}, },
@ -113,6 +115,9 @@ export default {
confirm(message) { confirm(message) {
return window.confirm(message); return window.confirm(message);
} }
},
mounted() {
this.scheduleAfterInit(() => [this.loadEventItems()]);
} }
}; };
</script> </script>

View file

@ -62,6 +62,7 @@ export default {
}, },
methods: { methods: {
...mapActions(['deleteItem', 'markItemReturned', 'sendMail', 'updateTicketPartial', 'postComment']), ...mapActions(['deleteItem', 'markItemReturned', 'sendMail', 'updateTicketPartial', 'postComment']),
...mapActions(['loadTickets', 'fetchTicketStates', 'loadUsers', 'scheduleAfterInit']),
handleMail(mail) { handleMail(mail) {
this.sendMail({ this.sendMail({
id: this.ticket.id, id: this.ticket.id,
@ -87,7 +88,9 @@ export default {
}) })
} }
}, },
created() {} mounted() {
this.scheduleAfterInit(() => [this.fetchTicketStates(), this.loadTickets(), this.loadUsers()]);
}
}; };
</script> </script>

View file

@ -65,7 +65,7 @@ export default {
...mapGetters(['stateInfo', 'getEventSlug', 'layout']), ...mapGetters(['stateInfo', 'getEventSlug', 'layout']),
}, },
methods: { methods: {
...mapActions(['loadTickets', 'fetchTicketStates']), ...mapActions(['loadTickets', 'fetchTicketStates', 'scheduleAfterInit']),
gotoDetail(ticket) { gotoDetail(ticket) {
this.$router.push({name: 'ticket', params: {id: ticket.id}}); this.$router.push({name: 'ticket', params: {id: ticket.id}});
}, },
@ -80,9 +80,8 @@ export default {
}; };
} }
}, },
created() { mounted() {
this.fetchTicketStates(); this.scheduleAfterInit(() => [this.fetchTicketStates(), this.loadTickets()]);
this.loadTickets();
} }
}; };
</script> </script>