stash
This commit is contained in:
parent
30cddebdc8
commit
4645a2f48c
5 changed files with 172 additions and 114 deletions
|
@ -36,7 +36,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['removeToast', 'createToast', 'closeAddBoxModal', 'openAddBoxModal']),
|
...mapMutations(['removeToast', 'createToast', 'closeAddBoxModal', 'openAddBoxModal']),
|
||||||
...mapActions(['loadEvents', 'afterLogin']),
|
...mapActions(['loadEvents']),
|
||||||
openAddItemModal() {
|
openAddItemModal() {
|
||||||
this.addItemModalOpen = true;
|
this.addItemModalOpen = true;
|
||||||
},
|
},
|
||||||
|
@ -53,9 +53,9 @@ export default {
|
||||||
created: function () {
|
created: function () {
|
||||||
document.title = document.location.hostname;
|
document.title = document.location.hostname;
|
||||||
this.loadEvents().then(() => {
|
this.loadEvents().then(() => {
|
||||||
if (this.isLoggedIn) {
|
/*if (this.isLoggedIn) {
|
||||||
this.afterLogin();
|
this.afterLogin();
|
||||||
}
|
}*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -165,6 +165,14 @@ export default (config) => {
|
||||||
if (config.debug) console.log('worker started');
|
if (config.debug) console.log('worker started');
|
||||||
|
|
||||||
const updateWorkerState = (key, new_value, old_value = null) => {
|
const updateWorkerState = (key, new_value, old_value = null) => {
|
||||||
|
if(new_value === old_value) {
|
||||||
|
if (config.debug) console.log('updateWorkerState: no change', key, new_value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (new_value === undefined) {
|
||||||
|
if (config.debug) console.log('updateWorkerState: undefined', key, new_value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
worker.port.postMessage({
|
worker.port.postMessage({
|
||||||
type: 'state_diff',
|
type: 'state_diff',
|
||||||
key: key,
|
key: key,
|
||||||
|
|
198
web/src/store.js
198
web/src/store.js
|
@ -1,60 +1,12 @@
|
||||||
import {createStore} from 'vuex';
|
import {createStore} from 'vuex';
|
||||||
import AxiosBootstrap from 'axios';
|
|
||||||
import router from './router';
|
import router from './router';
|
||||||
|
|
||||||
import * as base64 from 'base-64';
|
import * as base64 from 'base-64';
|
||||||
import * as utf8 from 'utf8';
|
import * as utf8 from 'utf8';
|
||||||
import {ticketStateColorLookup, ticketStateIconLookup} from "@/utils";
|
import {ticketStateColorLookup, ticketStateIconLookup, http} from "@/utils";
|
||||||
import sharedStatePlugin from "@/shared-state-plugin";
|
import sharedStatePlugin from "@/shared-state-plugin";
|
||||||
import persistentStatePlugin from "@/persistent-state-plugin";
|
import persistentStatePlugin from "@/persistent-state-plugin";
|
||||||
|
|
||||||
const axios = AxiosBootstrap.create({
|
|
||||||
baseURL: '/api',
|
|
||||||
});
|
|
||||||
axios.interceptors.response.use(response => response, error => {
|
|
||||||
if (error.response.status === 401) {
|
|
||||||
console.log('401 interceptor fired');
|
|
||||||
store.dispatch('reloadToken').then((ok) => {
|
|
||||||
if (ok) {
|
|
||||||
error.config.headers['Authorization'] = `Token ${store.state.token}`;
|
|
||||||
return axios.request(error.config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (error.response.status === 403) {
|
|
||||||
const message = `
|
|
||||||
<h3>Access denied.</h3>
|
|
||||||
<p>
|
|
||||||
url: ${error.config.url}
|
|
||||||
<br>
|
|
||||||
method: ${error.config.method}
|
|
||||||
<br>
|
|
||||||
response-body: ${error.response && error.response.body}
|
|
||||||
</p>
|
|
||||||
`;
|
|
||||||
store.commit('createToast', {title: 'Error: Access denied', message, color: 'danger'});
|
|
||||||
return Promise.reject(error)
|
|
||||||
} else {
|
|
||||||
console.error('error interceptor fired', error.message);
|
|
||||||
|
|
||||||
if (error.isAxiosError) {
|
|
||||||
const message = `
|
|
||||||
<h3>A HTTP ${error.config.method} request failed.</h3>
|
|
||||||
<p>
|
|
||||||
url: ${error.config.url}
|
|
||||||
<br>
|
|
||||||
timeout: ${!!error.request.timeout}
|
|
||||||
<br>
|
|
||||||
response-body: ${error.response && error.response.body}
|
|
||||||
</p>
|
|
||||||
`;
|
|
||||||
store.commit('createToast', {title: 'Error: HTTP', message, color: 'danger'});
|
|
||||||
} else {
|
|
||||||
store.commit('createToast', {title: 'Error: Unknown', message: error.toString(), color: 'danger'});
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const store = createStore({
|
const store = createStore({
|
||||||
state: {
|
state: {
|
||||||
keyIncrement: 0,
|
keyIncrement: 0,
|
||||||
|
@ -213,8 +165,6 @@ const store = createStore({
|
||||||
user.expiry = null;
|
user.expiry = null;
|
||||||
user.permissions = null;
|
user.permissions = null;
|
||||||
state.user = user;
|
state.user = user;
|
||||||
if (state.route.name !== 'login')
|
|
||||||
router.push('/login');
|
|
||||||
},
|
},
|
||||||
setTest(state, test) {
|
setTest(state, test) {
|
||||||
state.test = test;
|
state.test = test;
|
||||||
|
@ -231,8 +181,7 @@ const store = createStore({
|
||||||
credentials: 'omit'
|
credentials: 'omit'
|
||||||
}).then(r => r.json())
|
}).then(r => r.json())
|
||||||
if (data && data.token) {
|
if (data && data.token) {
|
||||||
axios.defaults.headers.common['Authorization'] = `Token ${data.token}`;
|
const {data: {permissions}} = await http.get('/2/self/', data.token);
|
||||||
const {data: {permissions}} = await axios.get('/2/self/');
|
|
||||||
commit('setUserInfo', {...data, permissions, username, password});
|
commit('setUserInfo', {...data, permissions, username, password});
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -254,7 +203,6 @@ const store = createStore({
|
||||||
}).then(r => r.json()).catch(e => console.error(e))
|
}).then(r => r.json()).catch(e => console.error(e))
|
||||||
if (data && data.token) {
|
if (data && data.token) {
|
||||||
commit('setToken', data);
|
commit('setToken', data);
|
||||||
axios.defaults.headers.common['Authorization'] = `Token ${data.token}`;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,32 +213,31 @@ 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}) {
|
||||||
axios.defaults.headers.common['Authorization'] = `Token ${state.user.token}`;
|
//const boxes = dispatch('loadBoxes');
|
||||||
/*const boxes = dispatch('loadBoxes');
|
//const states = dispatch('fetchTicketStates');
|
||||||
const states = dispatch('fetchTicketStates');
|
//const items = dispatch('loadEventItems');
|
||||||
const items = dispatch('loadEventItems');
|
//const tickets = dispatch('loadTickets');
|
||||||
const tickets = dispatch('loadTickets');
|
//const user = dispatch('loadUserInfo');
|
||||||
const user = dispatch('loadUserInfo');
|
//await Promise.all([boxes, items, tickets, user, states]);
|
||||||
await Promise.all([boxes, items, tickets, user, states]);*/
|
|
||||||
if (!state.user.permissions) {
|
if (!state.user.permissions) {
|
||||||
const user = dispatch('loadUserInfo');
|
const user = dispatch('loadUserInfo');
|
||||||
await Promise.all([user]);
|
await Promise.all([user]);
|
||||||
}
|
}
|
||||||
},
|
},*/
|
||||||
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}) {
|
async loadUserInfo({commit, state}) {
|
||||||
const {data} = await axios.get('/2/self/');
|
const {data} = await http.get('/2/self/', state.user.token);
|
||||||
commit('setPermissions', data.permissions);
|
commit('setPermissions', data.permissions);
|
||||||
},
|
},
|
||||||
async loadEvents({commit}) {
|
async loadEvents({commit, state}) {
|
||||||
const {data} = await axios.get('/2/events/');
|
const {data} = await http.get('/2/events/', state.user.token);
|
||||||
commit('replaceEvents', data);
|
commit('replaceEvents', data);
|
||||||
},
|
},
|
||||||
async fetchTicketStates({commit}) {
|
async fetchTicketStates({commit, state}) {
|
||||||
const {data} = await axios.get('/2/tickets/states/');
|
const {data} = await http.get('/2/tickets/states/', state.user.token);
|
||||||
commit('replaceTicketStates', data);
|
commit('replaceTicketStates', data);
|
||||||
},
|
},
|
||||||
changeEvent({dispatch, getters, commit}, eventName) {
|
changeEvent({dispatch, getters, commit}, eventName) {
|
||||||
|
@ -310,94 +257,94 @@ const store = createStore({
|
||||||
if (slug in state.itemCache) {
|
if (slug in state.itemCache) {
|
||||||
commit('replaceLoadedItems', state.itemCache[slug]);
|
commit('replaceLoadedItems', state.itemCache[slug]);
|
||||||
}
|
}
|
||||||
const {data} = await axios.get(`/2/${slug}/items/`);
|
const {data} = await http.get(`/2/${slug}/items/`, state.user.token);
|
||||||
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");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async searchEventItems({commit, getters}, query) {
|
async searchEventItems({commit, getters, state}, query) {
|
||||||
const foo = utf8.encode(query);
|
const foo = utf8.encode(query);
|
||||||
const bar = base64.encode(foo);
|
const bar = base64.encode(foo);
|
||||||
|
|
||||||
const {data} = await axios.get(`/2/${getters.getEventSlug}/items/${bar}/`);
|
const {data} = await http.get(`/2/${getters.getEventSlug}/items/${bar}/`, state.user.token);
|
||||||
commit('replaceLoadedItems', data);
|
commit('replaceLoadedItems', data);
|
||||||
},
|
},
|
||||||
async loadBoxes({commit}) {
|
async loadBoxes({commit, state}) {
|
||||||
const {data} = await axios.get('/2/boxes/');
|
const {data} = await http.get('/2/boxes/', state.user.token);
|
||||||
commit('replaceBoxes', data);
|
commit('replaceBoxes', data);
|
||||||
},
|
},
|
||||||
async createBox({commit, dispatch}, box) {
|
async createBox({commit, dispatch, state}, box) {
|
||||||
const {data} = await axios.post('/2/boxes/', box);
|
const {data} = 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');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async deleteBox({commit, dispatch}, box_id) {
|
async deleteBox({commit, dispatch, state}, box_id) {
|
||||||
await axios.delete(`/2/boxes/${box_id}/`);
|
await http.delete(`/2/boxes/${box_id}/`, state.user.token);
|
||||||
dispatch('loadBoxes');
|
dispatch('loadBoxes');
|
||||||
},
|
},
|
||||||
async updateItem({commit, getters}, item) {
|
async updateItem({commit, getters, state}, item) {
|
||||||
const {data} = await axios.put(`/2/${getters.getEventSlug}/item/${item.uid}/`, item);
|
const {data} = await http.put(`/2/${getters.getEventSlug}/item/${item.uid}/`, item, state.user.token);
|
||||||
commit('updateItem', data);
|
commit('updateItem', data);
|
||||||
},
|
},
|
||||||
async markItemReturned({commit, getters}, item) {
|
async markItemReturned({commit, getters, state}, item) {
|
||||||
await axios.patch(`/2/${getters.getEventSlug}/item/${item.uid}/`, {returned: true});
|
await http.patch(`/2/${getters.getEventSlug}/item/${item.uid}/`, {returned: true}, state.user.token);
|
||||||
commit('removeItem', item);
|
commit('removeItem', item);
|
||||||
},
|
},
|
||||||
async deleteItem({commit, getters}, item) {
|
async deleteItem({commit, getters, state}, item) {
|
||||||
await axios.delete(`/2/${getters.getEventSlug}/item/${item.uid}/`, item);
|
await http.delete(`/2/${getters.getEventSlug}/item/${item.uid}/`, item, state.user.token);
|
||||||
commit('removeItem', item);
|
commit('removeItem', item);
|
||||||
},
|
},
|
||||||
async postItem({commit, getters}, 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 axios.post(`/2/${getters.getEventSlug}/item/`, item);
|
const {data} = await http.post(`/2/${getters.getEventSlug}/item/`, item, state.user.token);
|
||||||
commit('appendItem', data);
|
commit('appendItem', data);
|
||||||
},
|
},
|
||||||
async loadTickets({commit}) {
|
async loadTickets({commit, state}) {
|
||||||
const {data} = await axios.get('/2/tickets/');
|
const {data} = await http.get('/2/tickets/', state.user.token);
|
||||||
commit('replaceTickets', data);
|
commit('replaceTickets', data);
|
||||||
},
|
},
|
||||||
async sendMail({commit, dispatch}, {id, message}) {
|
async sendMail({commit, dispatch, state}, {id, message}) {
|
||||||
const {data} = await axios.post(`/2/tickets/${id}/reply/`, {message});
|
const {data} = await http.post(`/2/tickets/${id}/reply/`, {message}, state.user.token);
|
||||||
await dispatch('loadTickets');
|
await dispatch('loadTickets');
|
||||||
},
|
},
|
||||||
async postManualTicket({commit, dispatch}, {sender, message, title,}) {
|
async postManualTicket({commit, dispatch, state}, {sender, message, title,}) {
|
||||||
const {data} = await axios.post(`/2/tickets/manual/`, {
|
const {data} = await http.post(`/2/tickets/manual/`, {
|
||||||
name: title,
|
name: title,
|
||||||
sender,
|
sender,
|
||||||
body: message,
|
body: message,
|
||||||
recipient: 'mail@c3lf.de'
|
recipient: 'mail@c3lf.de'
|
||||||
});
|
}, state.user.token);
|
||||||
await dispatch('loadTickets');
|
await dispatch('loadTickets');
|
||||||
},
|
},
|
||||||
async postComment({commit, dispatch}, {id, message}) {
|
async postComment({commit, dispatch, staee}, {id, message}) {
|
||||||
const {data} = await axios.post(`/2/tickets/${id}/comment/`, {comment: message});
|
const {data} = await http.post(`/2/tickets/${id}/comment/`, {comment: message}, state.user.token);
|
||||||
await dispatch('loadTickets');
|
await dispatch('loadTickets');
|
||||||
},
|
},
|
||||||
async loadUsers({commit}) {
|
async loadUsers({commit, state}) {
|
||||||
const {data} = await axios.get('/2/users/');
|
const {data} = await http.get('/2/users/', state.user.token);
|
||||||
commit('replaceUsers', data);
|
commit('replaceUsers', data);
|
||||||
},
|
},
|
||||||
async loadGroups({commit}) {
|
async loadGroups({commit, state}) {
|
||||||
const {data} = await axios.get('/2/groups/');
|
const {data} = await http.get('/2/groups/', state.user.token);
|
||||||
commit('replaceGroups', data);
|
commit('replaceGroups', data);
|
||||||
},
|
},
|
||||||
async updateTicket({commit}, ticket) {
|
async updateTicket({commit, state}, ticket) {
|
||||||
const {data} = await axios.put(`/2/tickets/${ticket.id}/`, ticket);
|
const {data} = await http.put(`/2/tickets/${ticket.id}/`, ticket, state.user.token);
|
||||||
commit('updateTicket', data);
|
commit('updateTicket', data);
|
||||||
},
|
},
|
||||||
async updateTicketPartial({commit}, {id, ...ticket}) {
|
async updateTicketPartial({commit, state}, {id, ...ticket}) {
|
||||||
const {data} = await axios.patch(`/2/tickets/${id}/`, ticket);
|
const {data} = 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: false,
|
debug: true,
|
||||||
isLoadedKey: "persistent_loaded",
|
isLoadedKey: "persistent_loaded",
|
||||||
state: [
|
state: [
|
||||||
"remember",
|
"remember",
|
||||||
|
@ -405,24 +352,57 @@ const store = createStore({
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
sharedStatePlugin({
|
sharedStatePlugin({
|
||||||
debug: false,
|
debug: true,
|
||||||
isLoadedKey: "shared_loaded",
|
isLoadedKey: "shared_loaded",
|
||||||
clearingMutation: "logout",
|
clearingMutation: "logout",
|
||||||
state: [
|
state: [
|
||||||
"test",
|
"test",
|
||||||
|
"state_options",
|
||||||
|
"events",
|
||||||
|
"fetchData",
|
||||||
"tickets",
|
"tickets",
|
||||||
|
],
|
||||||
|
watch: [
|
||||||
|
"test",
|
||||||
"state_options",
|
"state_options",
|
||||||
"events",
|
"events",
|
||||||
"fetchData",
|
"fetchData",
|
||||||
],
|
],
|
||||||
watch: [
|
|
||||||
"test",
|
|
||||||
],
|
|
||||||
mutations: [
|
mutations: [
|
||||||
"replaceTickets",
|
//"replaceTickets",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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) => {
|
||||||
|
console.log('user changed', user);
|
||||||
|
if (store.getters.isLoggedIn) {
|
||||||
|
if (store.state.route.name === 'login' && store.state.route.query.redirect)
|
||||||
|
router.push(store.state.route.query.redirect);
|
||||||
|
else if (store.state.route.name === 'login')
|
||||||
|
router.push('/');
|
||||||
|
} else {
|
||||||
|
if (store.state.route.name !== 'login') {
|
||||||
|
router.push({
|
||||||
|
name: 'login',
|
||||||
|
query: {redirect: store.state.route.fullPath},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
|
|
@ -24,4 +24,74 @@ function ticketStateIconLookup(ticket) {
|
||||||
return 'exclamation';
|
return 'exclamation';
|
||||||
}
|
}
|
||||||
|
|
||||||
export {ticketStateColorLookup, ticketStateIconLookup};
|
const http = {
|
||||||
|
post: async (url, data, token) => {
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const response = await fetch('/api' + url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Token ${data.token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
return {data: await response.json()};
|
||||||
|
}, get: async (url, token) => {
|
||||||
|
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) => {
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const response = await fetch('/api' + url, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Token ${data.token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
return {data: await response.json()};
|
||||||
|
},
|
||||||
|
patch: async (url, data, token) => {
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const response = await fetch('/api' + url, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Token ${data.token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
return {data: await response.json()};
|
||||||
|
},
|
||||||
|
delete: async (url, token) => {
|
||||||
|
if (!token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const response = await fetch('/api' + url, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Token ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {data: await response.json()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {ticketStateColorLookup, ticketStateIconLookup, http};
|
|
@ -100,7 +100,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
input{
|
input {
|
||||||
background-color: var(--dark);
|
background-color: var(--dark);
|
||||||
border: var(--gray) 1px solid;;
|
border: var(--gray) 1px solid;;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue