Compare commits

...

3 commits

Author SHA1 Message Date
22ee68a3bf Merge branch 'jedi/dev' into bton/dev 2024-11-02 00:32:49 +00:00
1fb25c796f add dev docker 2024-11-01 23:41:29 +01:00
546e49e50e stash events admin frontend 2024-11-01 22:26:15 +01:00
10 changed files with 210 additions and 9 deletions

1
deploy/dev/.backend.env Normal file
View file

@ -0,0 +1 @@
HTTP_HOST=core

View file

@ -0,0 +1,13 @@
FROM python:3.11-bookworm
LABEL authors="lagertonne"
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.dev.txt /code/
COPY requirements.prod.txt /code/
RUN apt update && apt install -y mariadb-client
RUN pip install -r requirements.dev.txt
RUN pip install -r requirements.prod.txt
RUN pip install mysqlclient
COPY .. /code/

View file

@ -0,0 +1,6 @@
FROM docker.io/node:22
RUN mkdir /web
WORKDIR /web
COPY package.json /web/
RUN npm install

View file

@ -0,0 +1,33 @@
services:
core:
build:
context: ../../core
dockerfile: ../deploy/dev/Dockerfile.backend
command: bash -c 'python manage.py migrate && python manage.py runserver 0.0.0.0:8000'
#environment:
# - DATABASE_URL
volumes:
- ../../core:/code
- ./.backend.env:/code/.env
ports:
- "8000:8000"
frontend:
build:
context: ../../web
dockerfile: ../deploy/dev/Dockerfile.frontend
command: npm run serve
volumes:
- ../../web:/web:ro
- /web/node_modules
- ./vue.config.js:/web/vue.config.js
ports:
- "8080:8080"
db:
image: mariadb
environment:
MARIADB_RANDOM_ROOT_PASSWORD: true
MARIADB_DATABASE: system3
MARIADB_USER: system3
MARIADB_PASSWORD: system3

27
deploy/dev/vue.config.js Normal file
View file

@ -0,0 +1,27 @@
// vue.config.js
module.exports = {
devServer: {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*"
},
proxy: {
'^/media/2': {
target: 'http://core:8000/',
},
'^/api/2': {
target: 'http://core:8000/',
},
'^/api/1': {
target: 'http://core:8000/',
},
'^/ws/2': {
target: 'http://core:8000/',
ws: true,
logLevel: 'debug',
},
}
}
}

View file

@ -3,6 +3,7 @@
<AddItemModal v-if="addItemModalOpen && isLoggedIn" @close="closeAddItemModal()" isModal="true"/>
<AddTicketModal v-if="addTicketModalOpen && isLoggedIn" @close="closeAddTicketModal()" isModal="true"/>
<AddBoxModal v-if="showAddBoxModal && isLoggedIn" @close="closeAddBoxModal()" isModal="true"/>
<AddEventModal v-if="showAddEventModal && isLoggedIn" @close="closeAddEventModal()" isModal="true"/>
<Navbar v-if="isLoggedIn" @addItemClicked="openAddItemModal()" @addTicketClicked="openAddTicketModal()"/>
<router-view style="flex: 1 1;"/>
</div>
@ -14,12 +15,13 @@ import AddItemModal from '@/components/AddItemModal';
import {mapState, mapMutations, mapActions, mapGetters} from 'vuex';
import AddTicketModal from "@/components/AddTicketModal.vue";
import AddBoxModal from "@/components/AddBoxModal.vue";
import AddEventModal from "@/components/AddEventModal.vue";
export default {
name: 'app',
components: {AddBoxModal, Navbar, AddItemModal, AddTicketModal},
components: {AddBoxModal, AddEventModal, Navbar, AddItemModal, AddTicketModal},
computed: {
...mapState(['loadedItems', 'layout', 'toasts', 'showAddBoxModal']),
...mapState(['loadedItems', 'layout', 'toasts', 'showAddBoxModal', 'showAddEventModal']),
...mapGetters(['isLoggedIn']),
},
data: () => ({
@ -27,7 +29,7 @@ export default {
addTicketModalOpen: false
}),
methods: {
...mapMutations(['removeToast', 'createToast', 'closeAddBoxModal', 'openAddBoxModal']),
...mapMutations(['removeToast', 'createToast', 'closeAddBoxModal', 'openAddBoxModal', 'closeAddEventModal']),
...mapActions(['loadEvents', 'scheduleAfterInit']),
openAddItemModal() {
this.addItemModalOpen = true;

View file

@ -0,0 +1,86 @@
<template>
<div>
<Modal v-if="isModal" title="Add Event" @close="$emit('close')">
<template #body>
<div>
<div class="row mb-3">
<div class="col">
<input type="text" class="form-control" placeholder="Event Title"
v-model="data.name">
</div>
<div class="col">
<input type="text" class="form-control" placeholder="Event Slug" v-model="data.slug">
</div>
</div>
<div class="row">
<div class="col">
<input type="date" class="form-control" title="Buildup Start" v-model="data.pre_start"
:max="data.start" @focus="prepare_date_field">
</div>
<div class="col">
<input type="date" class="form-control" title="Official Event Start" v-model="data.start"
:min="data.pre_start" :max="data.end" @focus="prepare_date_field">
</div>
<div class="col">
<input type="date" class="form-control" title="Official Event End" v-model="data.end"
:min="data.start" :max="data.post_end" @focus="prepare_date_field">
</div>
<div class="col">
<input type="date" class="form-control" title="Teardown End" v-model="data.post_end"
:min="data.end" @focus="prepare_date_field">
</div>
</div>
<small>The buildup and teardown dates only relate to the timeframe in which the c3lf team is
present.</small>
</div>
</template>
<template #buttons>
<button type="button" class="btn btn-secondary" @click="$emit('close')">Cancel</button>
<button type="button" class="btn btn-success" @click="createEvent(data).then(()=>$emit('close'))">
Create
</button>
</template>
</Modal>
</div>
</template>
<script>
import Modal from '@/components/Modal';
import {mapActions} from "vuex";
export default {
name: 'AddEventModal',
components: {Modal},
props: ['isModal'],
data: () => ({
data: {
name: '',
slug: '',
start: null,
end: null,
pre_start: null,
post_end: null,
},
errors: {},
}),
methods: {
...mapActions(['createEvent']),
validate_data() {
return this.pre_start <= this.start && this.start <= this.end && this.end <= this.post_end && !!this.event_slug && !!this.event_name;
},
prepare_date_field(e) {
if (!e.target.value) {
if (e.target.min) {
e.target.value = e.target.min
} else if (e.target.max) {
e.target.value = e.target.max
}
}
}
},
};
</script>
<style scoped>
</style>

View file

@ -54,6 +54,7 @@ const store = createStore({
afterInitHandlers: [],
showAddBoxModal: false,
showAddEventModal: false,
test: ['foo', 'bar', 'baz'],
shippingVoucherTypes: {
@ -179,6 +180,12 @@ const store = createStore({
closeAddBoxModal(state) {
state.showAddBoxModal = false;
},
openAddEventModal(state) {
state.showAddEventModal = true;
},
closeAddEventModal(state) {
state.showAddEventModal = false;
},
createToast(state, {title, message, color}) {
var toast = {title, message, color, key: state.keyIncrement}
state.toasts.push(toast);
@ -321,6 +328,18 @@ const store = createStore({
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 deleteEvent({commit, dispatch, state}, event_id) {
const {data, success} = await http.delete(`/2/events/${event_id}/`, state.user.token);
if (success) {
await dispatch('loadEvents')
commit('replaceEvents', [...state.events.filter(e => e.eid !== event_id)])
}
},
async fetchTicketStates({commit, state}) {
if (!state.user.token) return;
if (state.fetchedData.states > Date.now() - 1000 * 60 * 60 * 24) return;

View file

@ -95,8 +95,8 @@ const http = {
"Authorization": `Token ${token}`,
},
});
const success = response.status === 200 || response.status === 201;
return {data: await response.json() || {}, success};
const success = response.status === 204;
return {data: await response.text() || {}, success};
}
}

View file

@ -4,13 +4,19 @@
:items="events"
:keyName="'slug'"
>
<template #actions="{ item }">
<template v-slot:header_actions>
<button class="btn btn-success" @click.prevent="openAddEventModal">
<font-awesome-icon icon="plus"/>
Create Event
</button>
</template>
<template v-slot:actions="{ item }">
<div class="btn-group">
<button class="btn btn-secondary" @click.stop="changeEvent(item)">
<font-awesome-icon icon="archive"/>
use
</button>
<button class="btn btn-danger" @click.stop="">
<button class="btn btn-danger" @click.stop="safeDeleteEvent(item.eid)">
<font-awesome-icon icon="trash"/>
delete
</button>
@ -20,14 +26,22 @@
</template>
<script>
import {mapActions, mapState} from 'vuex';
import {mapActions, mapMutations, mapState} from 'vuex';
import Table from '@/components/Table';
export default {
name: 'Events',
components: {Table},
computed: mapState(['events']),
methods: mapActions(['changeEvent']),
methods: {
...mapActions(['changeEvent', 'deleteEvent']),
...mapMutations(['openAddEventModal']),
safeDeleteEvent(id) {
if (confirm('do you want to completely delete this event and related data?')) {
this.deleteEvent(id)
}
},
},
};
</script>