stash
This commit is contained in:
parent
4645a2f48c
commit
be96901129
10 changed files with 272 additions and 111 deletions
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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_')) {
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
158
web/src/store.js
158
web/src/store.js
|
@ -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) {
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue