First work on transfer services
This commit is contained in:
parent
8b6fd67f1d
commit
ea64568e2b
12
package.json
12
package.json
|
@ -23,20 +23,20 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@flaschengeist/types": "^1.0.0-alpha.5",
|
"@flaschengeist/types": "^1.0.0-alpha.5",
|
||||||
"@quasar/app": "^3.2.2",
|
"@quasar/app": "^3.2.3",
|
||||||
"quasar": "^2.3.2",
|
"quasar": "^2.3.3",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
"typescript": "^4.4.4",
|
"typescript": "^4.4.4",
|
||||||
"pinia": "^2.0.3",
|
"pinia": "^2.0.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||||
"@typescript-eslint/parser": "^5.4.0",
|
"@typescript-eslint/parser": "^5.4.0",
|
||||||
"eslint": "^8.2.0",
|
"eslint": "^8.3.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-vue": "^8.0.3"
|
"eslint-plugin-vue": "^8.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@flaschengeist/api": "^1.0.0-alpha.2",
|
"@flaschengeist/api": "^1.0.0-alpha.4",
|
||||||
"@flaschengeist/users": "^1.0.0-alpha.1"
|
"@flaschengeist/users": "^1.0.0-alpha.1"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
|
|
|
@ -27,6 +27,7 @@ declare namespace FG {
|
||||||
comment?: string;
|
comment?: string;
|
||||||
services: Array<Service>;
|
services: Array<Service>;
|
||||||
required_services: number;
|
required_services: number;
|
||||||
|
locked: boolean;
|
||||||
}
|
}
|
||||||
interface JobType {
|
interface JobType {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
|
@ -27,8 +27,29 @@
|
||||||
>
|
>
|
||||||
</q-select>
|
</q-select>
|
||||||
<div class="row col-12 justify-end">
|
<div class="row col-12 justify-end">
|
||||||
<q-btn v-if="canEnroll" flat color="primary" label="Eintragen" @click="enrollForJob" />
|
<q-btn
|
||||||
<q-btn v-if="isEnrolled" flat color="negative" label="Austragen" @click="signOutFromJob" />
|
v-if="!modelValue.locked && !isEnrolled && !isFull"
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
label="Eintragen"
|
||||||
|
@click="enrollForJob"
|
||||||
|
/>
|
||||||
|
<q-btn v-if="isEnrolled && !modelValue.locked" flat color="secondary" label="Optionen">
|
||||||
|
<q-menu auto-close>
|
||||||
|
<q-list style="min-width: 100px">
|
||||||
|
<q-item v-if="!isFull && canInvite" clickable @click="invite">
|
||||||
|
<q-item-section>Einladen</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable @click="transfer">
|
||||||
|
<q-item-section>Tauschen</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
<q-item clickable @click="signOutFromJob">
|
||||||
|
<q-item-section class="text-negative">Austragen</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
@ -36,9 +57,10 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onBeforeMount, computed, PropType } from 'vue';
|
import { defineComponent, onBeforeMount, computed, PropType } from 'vue';
|
||||||
import { useQuasar } from 'quasar';
|
import { date, useQuasar } from 'quasar';
|
||||||
import { asHour, useMainStore, useUserStore } from '@flaschengeist/api';
|
import { asHour, useMainStore, useUserStore } from '@flaschengeist/api';
|
||||||
import { useEventStore } from '../../../store';
|
import { useEventStore } from '../../../store';
|
||||||
|
import TransferInviteDialog from './TransferInviteDialog.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'JobSlot',
|
name: 'JobSlot',
|
||||||
|
@ -72,12 +94,20 @@ export default defineComponent({
|
||||||
) !== -1
|
) !== -1
|
||||||
);
|
);
|
||||||
|
|
||||||
const canEnroll = computed(() => {
|
const isFull = computed(
|
||||||
const is = isEnrolled.value;
|
() =>
|
||||||
let sum = 0;
|
props.modelValue.services.map((s) => s.value).reduce((p, c) => p + c, 0) >=
|
||||||
props.modelValue.services.forEach((s) => (sum += s.value));
|
props.modelValue.required_services
|
||||||
return sum < props.modelValue.required_services && !is;
|
);
|
||||||
});
|
|
||||||
|
const canInvite = computed(
|
||||||
|
() =>
|
||||||
|
(props.modelValue.end || props.modelValue.start) >
|
||||||
|
date.subtractFromDate(
|
||||||
|
date.buildDate({ hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }),
|
||||||
|
{ days: 1 }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
async function enrollForJob() {
|
async function enrollForJob() {
|
||||||
const newService: FG.Service = {
|
const newService: FG.Service = {
|
||||||
|
@ -86,7 +116,9 @@ export default defineComponent({
|
||||||
value: 1,
|
value: 1,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const job = await store.updateJob(props.eventId, props.modelValue.id, { user: newService });
|
await store.assignToJob(props.modelValue.id, newService);
|
||||||
|
const job = Object.assign({}, props.modelValue)
|
||||||
|
job.services.push(newService)
|
||||||
emit('update:modelValue', job);
|
emit('update:modelValue', job);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(error);
|
console.warn(error);
|
||||||
|
@ -107,9 +139,9 @@ export default defineComponent({
|
||||||
value: -1,
|
value: -1,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const job = await store.updateJob(props.eventId, props.modelValue.id, {
|
await store.assignToJob(props.modelValue.id, newService);
|
||||||
user: newService,
|
const job = Object.assign({}, props.modelValue)
|
||||||
});
|
job.services.push(newService)
|
||||||
emit('update:modelValue', job);
|
emit('update:modelValue', job);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(error);
|
console.warn(error);
|
||||||
|
@ -124,12 +156,24 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function invite(isInvite = true) {
|
||||||
|
quasar.dialog({
|
||||||
|
component: TransferInviteDialog,
|
||||||
|
componentProps: {
|
||||||
|
invite: isInvite,
|
||||||
|
job: props.modelValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availableUsers,
|
canInvite,
|
||||||
enrollForJob,
|
enrollForJob,
|
||||||
isEnrolled,
|
isEnrolled,
|
||||||
|
isFull,
|
||||||
|
invite: () => invite(true),
|
||||||
signOutFromJob,
|
signOutFromJob,
|
||||||
canEnroll,
|
transfer: () => invite(false),
|
||||||
userDisplay,
|
userDisplay,
|
||||||
asHour,
|
asHour,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<!-- notice dialogRef here -->
|
||||||
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<q-card class="q-dialog-plugin">
|
||||||
|
<q-card-section>
|
||||||
|
<div v-if="isInvite" class="text-h6">Zum Dienst einladen</div>
|
||||||
|
<div v-else class="text-h6">Dienst tauschen</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-separator />
|
||||||
|
<q-card-section>
|
||||||
|
<q-select
|
||||||
|
v-model="invitees"
|
||||||
|
filled
|
||||||
|
:options="otherUsers"
|
||||||
|
:option-label="(opt) => opt.display_name"
|
||||||
|
:multiple="isInvite"
|
||||||
|
use-chips
|
||||||
|
stack-label
|
||||||
|
label="Dienste"
|
||||||
|
>
|
||||||
|
</q-select>
|
||||||
|
</q-card-section>
|
||||||
|
<!-- buttons example -->
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn color="primary" label="Ok" @click="invite" />
|
||||||
|
<q-btn color="primary" label="Abbrechen" @click="onDialogCancel" />
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useMainStore, useUserStore } from '@flaschengeist/api';
|
||||||
|
import { useEventStore } from '../../../store';
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
import { PropType, computed, defineComponent, ref } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
isInvite: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
job: {
|
||||||
|
required: true,
|
||||||
|
type: Object as PropType<FG.Job>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: [...useDialogPluginComponent.emits],
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent();
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const mainStore = useMainStore();
|
||||||
|
const store = useEventStore();
|
||||||
|
const invitees = ref([] as FG.User[]);
|
||||||
|
const otherUsers = computed(() =>
|
||||||
|
userStore.users.filter(
|
||||||
|
(u) =>
|
||||||
|
u.userid !== mainStore.currentUser.userid &&
|
||||||
|
props.job.services.findIndex((s) => s.userid === u.userid) === -1
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
function invite() {
|
||||||
|
store
|
||||||
|
.sendInvite(props.job, invitees.value, !props.isInvite)
|
||||||
|
.then(() => onDialogOK())
|
||||||
|
.catch(() => onDialogCancel());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
invite,
|
||||||
|
invitees,
|
||||||
|
otherUsers,
|
||||||
|
dialogRef,
|
||||||
|
onDialogHide,
|
||||||
|
onDialogCancel,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -13,4 +13,8 @@ export const PERMISSIONS = {
|
||||||
ASSIGN: 'events_assign',
|
ASSIGN: 'events_assign',
|
||||||
// Can assign other users to jobs
|
// Can assign other users to jobs
|
||||||
ASSIGN_OTHER: 'events_assign_other',
|
ASSIGN_OTHER: 'events_assign_other',
|
||||||
|
// Can see users assigned as backup
|
||||||
|
SEE_BACKUP: 'events_see_backup',
|
||||||
|
// Can lock jobs, no further services can be assigned or unassigned
|
||||||
|
LOCK_JOBS: 'events_lock_jobs',
|
||||||
};
|
};
|
||||||
|
|
16
src/store.ts
16
src/store.ts
|
@ -138,14 +138,8 @@ export const useEventStore = defineStore({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateJob(eventId: number, jobId: number, service: FG.Service | UserService) {
|
async assignToJob(jobId: number, service: FG.Service) {
|
||||||
try {
|
return api.post<FG.Job>(`/events/jobs/${jobId}/assign`, service);
|
||||||
const { data } = await api.put<FG.Job>(`/events/${eventId}/jobs/${jobId}`, service);
|
|
||||||
fixJob(data);
|
|
||||||
return data;
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async addEvent(event: FG.Event) {
|
async addEvent(event: FG.Event) {
|
||||||
|
@ -161,5 +155,11 @@ export const useEventStore = defineStore({
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async sendInvite(job: FG.Job, invitees: FG.User[], transfer = false) {
|
||||||
|
await Promise.resolve()
|
||||||
|
console.log(job, invitees, transfer)
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue