6 Commits
Author SHA1 Message Date
torben 921f27e4d8 deployment stuff 2020-02-19 21:49:28 +01:00
torben 40bd02a2ed added production settings 2020-02-19 20:21:45 +01:00
Torben Indorf b84983ccef added the color picker for the states
create copys before editing the user, area, entity and state
2020-02-16 12:30:03 +01:00
Torben Indorf 4e1a51c823 added new icons for the navigation
select the new icon for any state
2020-02-11 21:56:57 +01:00
Torben Indorf a109a01959 create entity-stuff 2020-02-02 15:22:20 +01:00
Torben Indorf d9f14a3410 create user stuff 2020-02-02 14:25:54 +01:00
40 changed files with 999 additions and 132 deletions
+10
View File
@@ -0,0 +1,10 @@
VUE_APP_TITLE=GelegeApp - Dev
VUE_APP_BASE_URL= http://dev-api.lifemap.rocks/
VUE_APP_AUTH_URL= auth/
VUE_APP_I18N_LOCALE=de
VUE_APP_I18N_FALLBACK_LOCALE=en
VUE_APP_DEFAULT_USERNAME=t.indorf@28apps.de
VUE_APP_DEFAULT_PASSWORD=test
+8
View File
@@ -0,0 +1,8 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
+1
View File
@@ -0,0 +1 @@
ssh -t gelege-app 'bash -i -c "cd /var/www/html/dev/gelegeapp-frontend && git pull && npm install && npm run build && cp .htaccess dist/.htaccess"'
+5
View File
@@ -0,0 +1,5 @@
// TODO: Hinzufügen der "X erstellen"-Dialoge
// TODO: Hinzufügen der GeoJson zu einer Area (im Dialog + Backend)
// TODO: Logout
// TODO: InWork: Icons für Status
//
+5 -1
View File
@@ -1,12 +1,16 @@
import AuthRepository from '@/api/repositories/AuthRepository';
import AreaRepository from '@/api/repositories/AreaRepository';
import UserRepository from '@/api/repositories/UserRepository';
import StatusRepository from '@/api/repositories/StatusRepository';
import EntityRepository from '@/api/repositories/EntityRepository';
const repositories: any = {
auth: {repository: AuthRepository, instance: null},
area: {repository: AreaRepository, instance: null},
user: {repository: UserRepository, instance: null}
user: {repository: UserRepository, instance: null},
status: {repository: StatusRepository, instance: null},
entity: {repository: EntityRepository, instance: null}
};
/**
+2 -2
View File
@@ -15,10 +15,10 @@ export default class AreaRepository extends BaseRepository {
}
public editArea(area: any): Promise<any> {
return this.axiosClient.put(`area/${area.id}`, area);
return this.axiosClient.patch(`area/${area.id}`, area);
}
public setUsers(area: any, userIds: any): Promise<any> {
return this.axiosClient.post(`area/${area.id}/setUsers`, {userIds: userIds});
return this.axiosClient.post(`area/${area.id}/setUsers`, {userIds});
}
}
+20
View File
@@ -0,0 +1,20 @@
import BaseRepository from '@/api/repositories/BaseRepository';
export default class EntityRepository extends BaseRepository {
public loadAllEntities(): Promise<any> {
return this.axiosClient(`entity`);
}
public loadEntity(entityId: string): Promise<any> {
return this.axiosClient(`entity/${entityId}`);
}
public createEntity(entity: any): Promise<any> {
return this.axiosClient.post(`entity`, entity);
}
public editEntity(entity: any): Promise<any> {
return this.axiosClient.patch(`entity/${entity.id}`, entity);
}
}
+32
View File
@@ -0,0 +1,32 @@
import BaseRepository from '@/api/repositories/BaseRepository';
import {Status} from '@/models/Status';
export default class StatusRepository extends BaseRepository {
public loadAllStatus(): Promise<any> {
return this.axiosClient(`status`);
}
public loadStatus(statusId: string): Promise<any> {
return this.axiosClient(`status/${statusId}`);
}
public createStatus(status: any): Promise<any> {
return this.axiosClient.post(`status`, status);
}
public editStatus(status: any): Promise<any> {
return this.axiosClient.patch(`status/${status.id}`, status);
}
public setStatusIcon(status: Status, icon: any): Promise<any> {
const formData = new FormData();
formData.append('file', icon);
return this.axiosClient.post(`status/${status.id}/setIcon`, formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
}
+1 -1
View File
@@ -15,6 +15,6 @@ export default class UserRepository extends BaseRepository {
}
public editUser(user: any): Promise<any> {
return this.axiosClient.put(`user/${user.id}`, user);
return this.axiosClient.patch(`user/${user.id}`, user);
}
}
-23
View File
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: sketchtool 58 (101010) - https://sketch.com -->
<title>E20E0719-69F7-4807-B0EB-A3B8E520CFE4</title>
<desc>Created with sketchtool.</desc>
<g id="Admin-V2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Admin-/-Firma-dashboard" transform="translate(-16.000000, -18.000000)" fill="#FFFFFF">
<g id="Group-8">
<g id="Group-[74h]" transform="translate(16.000000, 18.000000)">
<g id="REIN_demo_logo">
<g id="Group-[26h]">
<g id="logo_white_v4">
<path d="M14.1487224,3.53718059 C18.0557842,3.53718059 21.2230835,6.70447998 21.2230835,10.6115418 C21.2230835,14.5186036 18.0557842,17.685903 14.1487224,17.685903 C10.2416606,17.685903 7.07436118,14.5186036 7.07436118,10.6115418 C7.07436118,10.4561071 7.07937403,10.3018433 7.08924687,10.1489031 C7.30539746,13.3710794 9.98756002,15.9173127 13.2644272,15.9173127 C16.6831063,15.9173127 19.4544933,13.1459257 19.4544933,9.72724663 C19.4544933,6.45037943 16.90826,3.76821687 13.6863698,3.55133527 L13.9104595,3.54111743 L13.9104595,3.54111743 Z" id="Oval-6" transform="translate(14.148722, 10.611542) rotate(170.000000) translate(-14.148722, -10.611542) "></path>
<path d="M15.4751651,1.7685903 C21.0915664,1.7685903 25.6445593,6.32158316 25.6445593,11.9379845 C25.6445593,17.5543858 21.0915664,22.1073787 15.4751651,22.1073787 C9.85876376,22.1073787 5.30577089,17.5543858 5.30577089,11.9379845 C5.30577089,11.7070202 5.31347053,11.4778542 5.32862713,11.2507292 C5.65638204,15.8365026 9.48003871,19.4544933 14.1487224,19.4544933 C19.0325496,19.4544933 22.9916738,15.495369 22.9916738,10.6115418 C22.9916738,5.94285811 19.3736832,2.11920145 14.7885103,1.79138013 C15.0150348,1.77628994 15.2442008,1.7685903 15.4751651,1.7685903 Z"></path>
<path d="M13.7065748,3.55271368e-14 C14.0156412,3.55271368e-14 14.3223185,0.010229409 14.6262864,0.030368126 C8.4293416,0.465007926 3.53718059,5.6301835 3.53718059,11.9379845 C3.53718059,18.5311513 8.88199831,23.875969 15.4751651,23.875969 C21.7822899,23.875969 26.9470342,18.9848568 27.3833037,12.788727 C27.4029648,13.0921718 27.4131496,13.3981839 27.4131496,13.7065748 C27.4131496,21.276507 21.276507,27.4131496 13.7065748,27.4131496 C6.13664256,27.4131496 3.55271368e-14,21.276507 3.55271368e-14,13.7065748 C3.55271368e-14,6.13664256 6.13664256,3.55271368e-14 13.7065748,3.55271368e-14 Z" id="Oval-8"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

+1 -7
View File
@@ -10,7 +10,7 @@
</template>
</v-data-table>
<create-area-component v-if="showEditAreaDialog" :showDialog="showEditAreaDialog" :selectedArea="selectedArea"
@createdArea="createdArea" @closeDialog="showEditAreaDialog = false">
@closeDialog="showEditAreaDialog = false">
</create-area-component>
<area-users-component v-if="showUsersDialog" :showDialog="showUsersDialog" :selectedArea="selectedArea"
@savedAreaUsers="savedAreaUsers" @closeDialog="showUsersDialog = false">
@@ -86,12 +86,6 @@
this.selectedArea = selectedArea;
this.showUsersDialog = true;
}
private createdArea() {
this.loadAreas();
this.showEditAreaDialog = false;
}
}
</script>
+23 -13
View File
@@ -1,26 +1,28 @@
<template>
<v-dialog title="Gebiet erstellen" v-model="showDialog" width="500" @close="closeDialog"
<v-dialog v-model="showDialog" width="500" @close="closeDialog"
@click:outside="closeDialog">
<v-card class="pa-6 white">
<div class="headline text-center mb-6">Gebiet erstellen
<div class="headline text-center mb-6">
{{ areaCopy.id ? 'Gebiet bearbeiten' : 'Gebiet erstellen' }}
</div>
<v-card-text>
<v-form class="d-flex flex-column">
<v-row class="d-flex flex-column">
<v-text-field hide-details outlined placeholder="Gebietsname" name="name" data-vv-as="Gebietsname"
<v-text-field hide-details outlined placeholder="Gebietsname" name="name"
data-vv-as="Gebietsname"
v-validate="'required'"
v-model="selectedArea.name"/>
v-model="areaCopy.name"/>
<span class="input-error text-center">{{ errors.first('name') }}</span>
</v-row>
<v-row>
<v-checkbox label="Aktiv" v-model="selectedArea.active"/>
<v-checkbox label="Aktiv" v-model="areaCopy.active"/>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn color="error" @click="closeDialog">Abbrechen</v-btn>
<v-btn color="primary" @click="saveArea">
{{ selectedArea.id ? 'Gebiet speichern' : 'Gebiet erstellen' }}
{{ areaCopy.id ? 'Gebiet speichern' : 'Gebiet erstellen' }}
</v-btn>
</v-card-actions>
</v-card>
@@ -28,7 +30,7 @@
</template>
<script lang="ts">
import {Component, Vue, Prop} from 'vue-property-decorator';
import {Component, Vue, Prop, Watch} from 'vue-property-decorator';
import {Area} from '@/models/Area';
import {namespace} from 'vuex-class';
@@ -40,14 +42,21 @@
@Prop({default: false})
private showDialog?: boolean;
@Prop({default: () => new Area(null)})
@Prop({default: () => new Area({})})
public selectedArea?: Area;
public areaCopy: Area = new Area({});
@AreaStore.Action('createAreaAction')
private createAreaAction!: (areaData: any) => Promise<Area[]>;
@AreaStore.Action('editAreaAction')
private editAreaAction!: (areaData: any) => Promise<Area[]>;
private editAreaAction!: (areaData: any) => Promise<Area>;
@Watch('selectedArea', {immediate: true})
private onAreaChange() {
this.areaCopy = this.selectedArea!.deepCopy();
}
private closeDialog() {
this.$emit('closeDialog');
@@ -56,13 +65,14 @@
private saveArea() {
this.$validator.validateAll().then(async (result) => {
if (result) {
if (this.selectedArea!.id) {
await this.editAreaAction(this.selectedArea);
if (this.areaCopy!.id) {
await this.editAreaAction(this.areaCopy);
} else {
await this.createAreaAction(this.selectedArea);
await this.createAreaAction(this.areaCopy);
}
this.closeDialog();
} else {
// TODO: Show error message
}
});
}
@@ -0,0 +1,103 @@
<template>
<v-dialog v-model="showDialog" width="500"
@close="closeDialog"
@click:outside="closeDialog">
<v-card class="pa-6 white">
<div class="headline text-center mb-6">
{{ entityCopy.id ? 'Entität bearbeiten' : 'Entität erstellen' }}
</div>
<v-card-text>
<v-form class="d-flex flex-column">
<v-row class="d-flex flex-column">
<v-text-field hide-details outlined placeholder="Entität-Name"
name="name"
data-vv-as="Entität-Name"
v-validate="'required'"
v-model="entityCopy.name"/>
<span class="input-error text-center">{{ errors.first('name') }}</span>
</v-row>
<v-row class="d-flex align-center flex-column">
<v-label>Statusfarbe</v-label>
<div>
<v-color-picker :value="entityCopy.color" @update:color="colorSelected" class="ma-2"
:swatches="predefinedColors" show-swatches></v-color-picker>
</div>
</v-row>
</v-form>
</v-card-text>
<v-card-actions class="d-flex justify-space-between">
<v-btn color="primary" @click="saveEntity">
{{ entityCopy.id ? 'Entität speichern' : 'Entität erstellen' }}
</v-btn>
<v-btn color="error" @click="closeDialog">Abbrechen</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
import {Component, Vue, Prop, Watch} from 'vue-property-decorator';
import {namespace} from 'vuex-class';
import {Entity} from '@/models/Entity';
const EntityStore = namespace('entity');
@Component({})
export default class CreateEntityComponent extends Vue {
@Prop({default: false})
private showDialog?: boolean;
@Prop({default: () => new Entity({})})
public selectedEntity?: Entity;
public entityCopy: Entity = new Entity({});
@EntityStore.Action('createEntityAction')
private createEntityAction!: (entityData: any) => Promise<Entity[]>;
@EntityStore.Action('editEntityAction')
private editEntityAction!: (entityData: any) => Promise<Entity>;
private predefinedColors!: any[];
private created() {
this.predefinedColors = [['#FF0000', '#AA0000', '#550000'],
['#FFFF00', '#AAAA00', '#555500'], ['#00FF00', '#00AA00', '#005500'],
['#00FFFF', '#00AAAA', '#005555'], ['#0000FF', '#0000AA', '#000055']];
}
@Watch('selectedEntity', {immediate: true})
private onEntityChange() {
this.entityCopy = {...this.selectedEntity};
}
public colorSelected(color: any) {
this.entityCopy!.color = color.hexa;
}
private closeDialog() {
this.$emit('closeDialog');
}
private saveEntity() {
this.$validator.validateAll().then(async (result) => {
if (result) {
if (this.entityCopy!.id) {
await this.editEntityAction(this.entityCopy);
} else {
await this.createEntityAction(this.entityCopy);
}
this.closeDialog();
} else {
// TODO: Show error message
}
});
}
}
</script>
<style lang="scss" scoped>
</style>
@@ -0,0 +1,87 @@
<template>
<div>
<v-data-table
:headers="tableHeaders" :items="allEntities" class="elevation-1" item-key="id">
<template v-slot:item.options="{ item }">
<div class="d-flex justify-end ma-3">
<v-btn color="primary" small @click="handleEdit(item)" class="mr-1">
<v-icon>mdi-pencil</v-icon>
</v-btn>
</div>
</template>
<template v-slot:item.color="{ item }">
<div v-bind:style="{ backgroundColor: item.color}" class="entityColor"></div>
</template>
</v-data-table>
<create-entity-component v-if="showEditEntityDialog" :showDialog="showEditEntityDialog"
:selectedEntity="selectedEntity" @closeDialog="showEditEntityDialog = false">
</create-entity-component>
</div>
</template>
<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
import {namespace} from 'vuex-class';
import {Entity} from '@/models/Entity';
const EntityStore = namespace('entity');
@Component({
components: {
CreateEntityComponent: () => import(
/* webpackChunkName: "CreateEntityComponent" */
'@/components/entity/CreateEntity.component.vue'
),
}
})
export default class EntityTableComponent extends Vue {
@EntityStore.Action('loadEntitiesAction')
private loadEntities!: () => Promise<Entity[]>;
@EntityStore.Getter('entities')
public allEntities?: Entity[];
public tableHeaders: any[] = [];
private showEditEntityDialog: boolean = false;
public selectedEntity?: Entity;
public created() {
this.tableHeaders = [{
text: 'Name der Entität',
value: 'name'
}, {
text: 'Beschreibung',
value: 'description'
}, {
text: 'Statusfarbe',
value: 'color'
}, {
text: 'Optionen',
value: 'options',
align: 'right',
width: 200
}];
this.loadEntities();
}
private handleEdit(selectedEntity: Entity) {
this.selectedEntity = selectedEntity;
this.showEditEntityDialog = true;
}
}
</script>
<style lang="scss" scoped>
.entityColor {
height: 25px;
width: 25px;
border-radius: 50%;
}
</style>
@@ -0,0 +1,105 @@
<template>
<v-dialog v-model="showDialog" width="500"
@close="closeDialog"
@click:outside="closeDialog">
<v-card class="pa-6 white">
<div class="headline text-center mb-6">
{{ statusCopy.id ? 'Status bearbeiten' : 'Status erstellen' }}
</div>
<v-card-text>
<v-form class="d-flex flex-column">
<v-row class="d-flex flex-column">
<v-text-field hide-details outlined placeholder="Statusname"
name="name"
data-vv-as="Statusname"
v-validate="'required'"
v-model="statusCopy.name"/>
<span class="input-error text-center">{{ errors.first('name') }}</span>
</v-row>
<v-row class="d-flex flex-column mt-6">
<v-label class="">Neues Icon auswählen</v-label>
<v-file-input v-model="iconFile" name="iconFile"
ref="iconFile" class="mt-3" label="Icon" accept="image/png" outlined>
</v-file-input>
<v-label v-if="statusCopy.iconName">Derzeitiges Icon: {{ statusCopy.iconName }}
</v-label>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn color="error" @click="closeDialog">Abbrechen</v-btn>
<v-btn color="primary" @click="saveStatus">
{{ statusCopy.id ? 'Status speichern' : 'Status erstellen' }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
import {Component, Vue, Prop, Watch} from 'vue-property-decorator';
import {namespace} from 'vuex-class';
import {Status} from '@/models/Status';
const StatusStore = namespace('status');
@Component({})
export default class CreateStatusComponent extends Vue {
@Prop({default: false})
private showDialog?: boolean;
@Prop({default: () => new Status({})})
public selectedStatus?: Status;
public statusCopy: Status = new Status({});
@StatusStore.Action('createStatusAction')
private createStatusAction!: (statusData: any) => Promise<Status>;
@StatusStore.Action('editStatusAction')
private editStatusAction!: (statusData: any) => Promise<Status>;
@StatusStore.Action('setStatusIconAction')
private setStatusIconAction!: (payload: { status: Status, icon: any }) => Promise<boolean>;
public iconFile?: any = null;
@Watch('selectedStatus', {immediate: true})
private onStatusChange() {
this.statusCopy = {...this.selectedStatus};
}
private closeDialog() {
this.$emit('closeDialog');
}
private saveStatus() {
this.$validator.validateAll().then(async (result) => {
if (result) {
if (this.statusCopy!.id) {
await this.editStatusAction(this.statusCopy);
} else {
this.statusCopy = await this.createStatusAction(this.statusCopy);
}
if (this.iconFile) {
await this.setStatusIconAction({
status: this.statusCopy!,
icon: this.iconFile
});
}
this.closeDialog();
} else {
// TODO: Show error message
}
});
}
}
</script>
<style lang="scss" scoped>
</style>
@@ -0,0 +1,80 @@
<template>
<div>
<v-data-table
:headers="tableHeaders" :items="allStatus" class="elevation-1" item-key="id">
<template v-slot:item.options="{ item }">
<div class="d-flex justify-end ma-3">
<v-btn color="primary" small @click="handleEdit(item)" class="mr-1">
<v-icon>mdi-pencil</v-icon>
</v-btn>
</div>
</template>
</v-data-table>
<create-status-component v-if="showEditStatusDialog" :showDialog="showEditStatusDialog"
:selectedStatus="selectedStatus" @closeDialog="showEditStatusDialog = false">
</create-status-component>
</div>
</template>
<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
import {namespace} from 'vuex-class';
import {Status} from '@/models/Status';
const StatusStore = namespace('status');
@Component({
components: {
CreateStatusComponent: () => import(
/* webpackChunkName: "CreateStatusComponent" */
'@/components/status/CreateStatus.component.vue'
),
}
})
export default class StatusTableComponent extends Vue {
@StatusStore.Action('loadStatusAction')
private loadStatus!: () => Promise<Status[]>;
@StatusStore.Getter('status')
public allStatus?: Status[];
public tableHeaders: any[] = [];
private showEditStatusDialog: boolean = false;
public selectedStatus?: Status;
public created() {
this.tableHeaders = [{
text: 'Statusname',
value: 'name'
}, {
text: 'Icon-Name',
value: 'iconName'
}, {
text: 'Optionen',
value: 'options',
align: 'right',
width: 200
}];
this.loadStatus();
}
private handleEdit(selectedStatus: Status) {
this.selectedStatus = selectedStatus;
this.showEditStatusDialog = true;
}
private createdStatus() {
this.loadStatus();
this.showEditStatusDialog = false;
}
}
</script>
<style lang="scss" scoped>
</style>
@@ -0,0 +1,103 @@
<template>
<v-dialog v-model="showDialog" width="500"
@close="closeDialog"
@click:outside="closeDialog">
<v-card class="pa-6 white">
<div class="headline text-center mb-6">
{{ userCopy.id ? 'Benutzer bearbeiten' : 'Benutzer erstellen' }}
</div>
<v-card-text>
<v-form class="d-flex flex-column">
<v-row class="d-flex flex-column">
<v-text-field hide-details outlined placeholder="Vorname"
name="firstName"
data-vv-as="Vorname"
v-validate="'required'"
v-model="userCopy.firstName"/>
<span class="input-error text-center">{{ errors.first('firstName') }}</span>
</v-row>
<v-row class="d-flex flex-column">
<v-text-field hide-details outlined placeholder="Nachname"
name="lastName"
data-vv-as="Nachname"
v-validate="'required'"
v-model="userCopy.lastName"/>
<span class="input-error text-center">{{ errors.first('lastName') }}</span>
</v-row>
<v-row class="d-flex flex-column">
<v-text-field hide-details outlined placeholder="E-Mail"
name="email"
data-vv-as="E-Mail"
v-validate="'required'"
v-model="userCopy.email"/>
<span class="input-error text-center">{{ errors.first('email') }}</span>
</v-row>
<v-row>
<v-checkbox label="Aktiv" v-model="userCopy.active"/>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn color="error" @click="closeDialog">Abbrechen</v-btn>
<v-btn color="primary" @click="saveUser">
{{ userCopy.id ? 'Benutzer speichern' : 'Benutzer erstellen' }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
import {Component, Vue, Prop, Watch} from 'vue-property-decorator';
import {namespace} from 'vuex-class';
import User from '@/models/User';
const UserStore = namespace('user');
@Component({})
export default class CreateUserComponent extends Vue {
@Prop({default: false})
private showDialog?: boolean;
@Prop({default: () => new User({})})
public selectedUser?: User;
public userCopy: User = new User({});
@UserStore.Action('createUserAction')
private createUserAction!: (userData: any) => Promise<User[]>;
@UserStore.Action('editUserAction')
private editUserAction!: (userData: any) => Promise<User>;
private closeDialog() {
this.$emit('closeDialog');
}
@Watch('selectedUser', {immediate: true})
private onUserChange() {
this.userCopy = this.selectedUser!.deepCopy();
}
private saveUser() {
this.$validator.validateAll().then(async (result) => {
if (result) {
if (this.userCopy!.id) {
await this.editUserAction(this.userCopy);
} else {
await this.createUserAction(this.userCopy);
}
this.closeDialog();
} else {
// TODO: Show error message
}
});
}
}
</script>
<style lang="scss" scoped>
</style>
@@ -0,0 +1,86 @@
<template>
<div>
<v-data-table
:headers="tableHeaders" :items="allUsers" class="elevation-1" item-key="id">
<template v-slot:item.options="{ item }">
<div class="d-flex justify-end ma-3">
<v-btn color="primary" small @click="handleEdit(item)" class="mr-1">
<v-icon>mdi-pencil</v-icon>
</v-btn>
</div>
</template>
</v-data-table>
<create-user-component v-if="showEditUserDialog" :showDialog="showEditUserDialog" :selectedUser="selectedUser"
@closeDialog="showEditUserDialog = false">
</create-user-component>
</div>
</template>
<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
import {namespace} from 'vuex-class';
import User from '@/models/User';
const UserStore = namespace('user');
@Component({
components: {
CreateUserComponent: () => import(
/* webpackChunkName: "CreateUserComponent" */
'@/components/user/CreateUser.component.vue'
),
}
})
export default class UserTableComponent extends Vue {
@UserStore.Action('loadUsersAction')
private loadUsers!: () => Promise<User[]>;
@UserStore.Getter('users')
public allUsers?: User[];
public tableHeaders: any[] = [];
private showEditUserDialog: boolean = false;
public selectedUser?: User;
public created() {
this.tableHeaders = [{
text: 'Vorname',
value: 'firstName'
}, {
text: 'Nachname',
value: 'lastName'
}, {
text: 'E-Mail',
value: 'email'
}, {
text: 'Gebiete',
value: 'areas.length'
}, {
text: 'Optionen',
value: 'options',
align: 'right',
width: 200
}];
this.loadUsers();
}
private handleEdit(selectedUser: User) {
this.selectedUser = selectedUser;
this.showEditUserDialog = true;
}
private createdUser() {
this.loadUsers();
this.showEditUserDialog = false;
}
}
</script>
<style lang="scss" scoped>
</style>
+17 -6
View File
@@ -5,16 +5,18 @@ export const NavigationItemStorage: NavigationItemInterface[] = [
{
title: i18n.t('GENERAL.DASHBOARD').toString(),
routerName: 'dashboard',
icon: 'mdi-chart-arc',
dropdownOptions: []
},
{
title: i18n.t('GENERAL.USERS').toString(),
routerName: 'userOverview',
icon: 'mdi-account',
dropdownOptions: [
{
title: 'NAVIGATION.DROPDOWN.ALL_USERS',
routerName: 'usersOverview',
icon: 'mdi-user'
icon: 'mdi-account-multiple'
},
{
title: 'NAVIGATION.DROPDOWN.CREATE_USER',
@@ -26,11 +28,12 @@ export const NavigationItemStorage: NavigationItemInterface[] = [
{
title: i18n.t('GENERAL.AREAS').toString(),
routerName: 'areasOverview',
icon: 'mdi-map',
dropdownOptions: [
{
title: 'NAVIGATION.DROPDOWN.ALL_AREAS',
routerName: 'areasOverview',
icon: 'mdi-account-multiple'
icon: 'mdi-map'
},
{
title: 'NAVIGATION.DROPDOWN.CREATE_AREA',
@@ -40,23 +43,31 @@ export const NavigationItemStorage: NavigationItemInterface[] = [
]
},
{
title: i18n.t('NAVIGATION.DROPDOWN.MISC').toString(),
title: i18n.t('NAVIGATION.DROPDOWN.ENTITIES').toString(),
routerName: 'entitiesOverview',
icon: 'mdi-binoculars',
dropdownOptions: [
{
title: 'NAVIGATION.DROPDOWN.ALL_ENTITIES',
routerName: 'entityOverview',
icon: 'mdi-account-multiple'
routerName: 'entitiesOverview',
icon: 'mdi-binoculars'
},
{
title: 'NAVIGATION.DROPDOWN.CREATE_ENTITY',
routerName: 'createEntity',
icon: 'mdi-plus-circle-outline'
}
]
},
{
title: i18n.t('NAVIGATION.DROPDOWN.STATUS').toString(),
routerName: 'statusOverview',
icon: 'mdi-state-machine',
dropdownOptions: [
{
title: 'NAVIGATION.DROPDOWN.ALL_STATUS',
routerName: 'statusOverview',
icon: 'mdi-account-multiple'
icon: 'mdi-state-machine'
},
{
title: 'NAVIGATION.DROPDOWN.CREATE_STATUS',
@@ -4,4 +4,5 @@ export interface NavigationItemInterface {
title: string;
routerName: string;
dropdownOptions: NavigationSubItemInterface[];
icon: string;
}
+2
View File
@@ -19,6 +19,8 @@
},
"NAVIGATION": {
"DROPDOWN": {
"ENTITIES": "Entitäten",
"STATUS": "Status",
"MISC": "Weiteres",
"ALL_USERS": "Alle Benutzer",
"ALL_AREAS": "Alle Gebiete",
+7 -3
View File
@@ -9,14 +9,18 @@ export class Area {
constructor(data: any) {
Object.assign(this, data);
if (data) {
this.users = [];
if (data.users) {
data.users.map((userData: any) => {
this.users = [];
data.users.forEach((userData: any) => {
this.users.push(new User(userData));
});
}
}
public deepCopy(): Area {
const copy = {...this};
copy.users = this.users;
return copy;
}
}
+10
View File
@@ -0,0 +1,10 @@
export class Entity {
public id?: string;
public name?: string;
public description?: string;
public color?: string;
constructor(data: any) {
Object.assign(this, data);
}
}
+10
View File
@@ -0,0 +1,10 @@
export class Status {
public id?: string;
public name?: string;
public iconName?: string;
constructor(data: any) {
Object.assign(this, data);
}
}
+6
View File
@@ -19,4 +19,10 @@ export default class User {
this.areas = data.areas.map((areaData: any) => new Area(areaData));
}
}
public deepCopy(): User {
const copy = {...this};
copy.areas = this.areas;
return copy;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
*/
export default {
// vuetify theme colors
'primary': '#3787f6',
'primary': '#00A252',
'secondary': '#F5F5F5',
'accent': '#ffffff', // TODO set accent color
'error': '#bf2828',
+1 -35
View File
@@ -5,8 +5,7 @@
* https://vuetifyjs.com/en/customization/theme#theme
*/
export default {
// REIN light theme colors
'primary': '#1867c0',
'primary': '#00A252',
'secondary': '#172B4D',
'accent': '#00A252',
'error': '#E02020',
@@ -31,37 +30,4 @@ export default {
'on-error': '#ffffff',
'on-warning': '#ffffff',
'on-info': '#ffffff',
// colors for mitarbeiter profile image
// ATTENTION: if employee colors are being updated, change the digitSum function in UserInitialsComponent accordingly.
'employee_color_1': '#4FC2F8',
'employee_color_2': '#FE8B66',
'employee_color_3': '#9CCC66',
'employee_color_4': '#9E9E9E',
'employee_color_5': '#A0887E',
'employee_color_6': '#4CD0E2',
'employee_color_7': '#4BB6AC',
'employee_color_8': '#5F97F6',
'employee_color_9': '#9FA8DB',
'employee_color_10': '#B39DDB',
// mics colors
'bar-yellow': '#F7B500',
'job-started': '#E9F7F2',
'admin-navigation-bar-bg': '#EDEFF5',
'signature-background': '#EEEEEE',
'map-radius-border' : '#B8C9E3',
'map-radius-background' : '#3282FC',
'button-success': '#02875A',
// dashboard icons
'total-customer': '#5CBBF6',
'total-employee': '#F7B500',
'total-location': '#B39DDB',
'total-workTime': '#00D086',
// chart
'chart-fill-color': 'rgba(0, 208, 134, 0.2)',
'chart-stroke-color': '#00D086',
// job states
'comment_color': '#FB8C00',
'cancelled_color': '#E02020',
'images_color': '#4CAF50',
};
+1 -1
View File
@@ -63,7 +63,7 @@ export default new Router({
{
path: 'entities',
name: 'entitiesOverview',
component: () => import('./views/entities/Entites.view.vue')
component: () => import('./views/entity/Entity.view.vue')
},
{
path: 'status',
+10
View File
@@ -4,6 +4,8 @@ import VuexPersistence from 'vuex-persist';
import authStore from './stores/auth.store';
import areaStore from '@/stores/area.store';
import userStore from '@/stores/user.store';
import statusStore from '@/stores/status.store';
import entityStore from '@/stores/entity.store';
Vue.use(Vuex);
@@ -28,6 +30,14 @@ const store: StoreOptions<any> = {
user: {
namespaced: true,
...userStore
},
status: {
namespaced: true,
...statusStore
},
entity: {
namespaced: true,
...entityStore
}
},
plugins: [vuexPersistence.plugin],
+2 -1
View File
@@ -30,7 +30,8 @@ const actions: ActionTree<any, any> = {
return area;
},
editAreaAction: async ({commit}, areaData: any): Promise<Area> => {
const areaRaw = await areaRepository.createArea(areaData);
delete areaData.users;
const areaRaw = await areaRepository.editArea(areaData);
const area = new Area(areaRaw.data);
commit('handleArea', area);
return area;
+62
View File
@@ -0,0 +1,62 @@
import {ActionTree, Module} from 'vuex';
import {RepositoryFactory} from '@/api/RepositoryFactory';
import {Entity} from '@/models/Entity';
import EntityRepository from '@/api/repositories/EntityRepository';
const entityRepository: EntityRepository = RepositoryFactory.get('entity');
const store = {
entities: [],
};
const actions: ActionTree<any, any> = {
loadEntitiesAction: async ({commit}): Promise<Entity[]> => {
const entitiesRaw = await entityRepository.loadAllEntities();
const entities = entitiesRaw.data.map((data: any) => new Entity(data));
commit('storeEntities', entities);
return entities;
},
loadSingleEntityAction: async ({commit}, entityId: string): Promise<Entity> => {
const entityRaw = await entityRepository.loadEntity(entityId);
const entity = new Entity(entityRaw.data);
return entity;
},
createEntityAction: async ({commit}, entityData: any): Promise<Entity> => {
const entityRaw = await entityRepository.createEntity(entityData);
const entity = new Entity(entityRaw.data);
commit('handleEntity', entity);
return entity;
},
editEntityAction: async ({commit}, entityData: any): Promise<Entity> => {
const entityRaw = await entityRepository.editEntity(entityData);
const entity = new Entity(entityRaw.data);
commit('handleEntity', entity);
return entity;
}
};
const mutations = {
storeEntities: (state: any, entities: Entity[]) => state.entities = entities,
handleEntity: (state: any, entity: Entity) => {
const index = state.entities.findIndex((item: Entity) => item.id === entity.id);
// replace or add entity object
if (index >= 0) {
state.entities.splice(index, 1, entity);
} else {
state.entities.push(entity);
}
}
};
const getters = {
entities: (state: any) => state.entities,
};
const entityStore: Module<any, any> = {
state: store,
actions,
mutations,
getters,
};
export default entityStore;
+68
View File
@@ -0,0 +1,68 @@
import {ActionTree, Module} from 'vuex';
import {RepositoryFactory} from '@/api/RepositoryFactory';
import StatusRepository from '@/api/repositories/StatusRepository';
import {Status} from '@/models/Status';
const statusRepository: StatusRepository = RepositoryFactory.get('status');
const store = {
status: [],
};
const actions: ActionTree<any, any> = {
loadStatusAction: async ({commit}): Promise<Status[]> => {
const statusRaw = await statusRepository.loadAllStatus();
const status = statusRaw.data.map((data: any) => new Status(data));
commit('storeStatus', status);
return status;
},
loadSingleStatusAction: async ({commit}, statusId: string): Promise<Status> => {
const statusRaw = await statusRepository.loadStatus(statusId);
const status = new Status(statusRaw.data);
return status;
},
createStatusAction: async ({commit}, statusData: any): Promise<Status> => {
const statusRaw = await statusRepository.createStatus(statusData);
const status = new Status(statusRaw.data);
commit('handleStatus', status);
return status;
},
editStatusAction: async ({commit}, statusData: any): Promise<Status> => {
const statusRaw = await statusRepository.editStatus(statusData);
const status = new Status(statusRaw.data);
commit('handleStatus', status);
return status;
},
setStatusIconAction: async ({commit}, payload: { status: Status, icon: any }): Promise<Status> => {
const statusRaw = await statusRepository.setStatusIcon(payload.status, payload.icon);
const status = new Status(statusRaw.data);
commit('handleStatus', status);
return status;
}
};
const mutations = {
storeStatus: (state: any, status: Status[]) => state.status = status,
handleStatus: (state: any, status: Status) => {
const index = state.status.findIndex((item: Status) => item.id === status.id);
// replace or add status object
if (index >= 0) {
state.status.splice(index, 1, status);
} else {
state.status.push(status);
}
}
};
const getters = {
status: (state: any) => state.status,
};
const statusStore: Module<any, any> = {
state: store,
actions,
mutations,
getters,
};
export default statusStore;
+2 -1
View File
@@ -28,7 +28,7 @@ const actions: ActionTree<any, any> = {
return user;
},
editUserAction: async ({commit}, userData: any): Promise<User> => {
const userRaw = await userRepository.createUser(userData);
const userRaw = await userRepository.editUser(userData);
const user = new User(userRaw.data);
commit('handleUser', user);
return user;
@@ -45,6 +45,7 @@ const mutations = {
} else {
state.users.push(user);
}
console.log('state', state.users);
}
};
+1 -6
View File
@@ -12,7 +12,7 @@
</div>
<area-table-component class="mt-3"/>
<create-area-component v-if="showCreateDialog" :showDialog="showCreateDialog"
@createdArea="createdArea" @closeDialog="showCreateDialog = false"/>
@closeDialog="showCreateDialog = false"/>
</div>
</template>
@@ -44,11 +44,6 @@
private openCreateDialog() {
this.showCreateDialog = true;
}
private createdArea() {
this.loadAreas();
this.showCreateDialog = false;
}
}
</script>
-16
View File
@@ -1,16 +0,0 @@
<template>
</template>
<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
@Component({})
export default class EntitesView extends Vue {
}
</script>
<style scoped lang="scss">
</style>
+45
View File
@@ -0,0 +1,45 @@
<template>
<div id="entity" class="ma-6">
<div class="d-flex justify-space-between">
<div class="right-header">
<h1>Entitäten</h1>
</div>
<div class="left-header">
<v-btn @click="openCreateDialog()" color="primary"
>Entität erstellen
</v-btn>
</div>
</div>
<entity-table-component class="mt-3"/>
<create-entity-component v-if="showCreateDialog" :showDialog="showCreateDialog"
@closeDialog="showCreateDialog = false"/>
</div>
</template>
<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
@Component({
components: {
EntityTableComponent: () => import(
/* webpackChunkName: "EntityTableComponent" */
'@/components/entity/EntityTable.component.vue'),
CreateEntityComponent: () => import(
/* webpackChunkName: "CreateEntityComponent" */
'@/components/entity/CreateEntity.component.vue'
)
}
})
export default class EntityView extends Vue {
private showCreateDialog: boolean = false;
private openCreateDialog() {
this.showCreateDialog = true;
}
}
</script>
<style scoped lang="scss">
</style>
+1 -1
View File
@@ -7,7 +7,7 @@
<div class="mt-12"></div>
<div class="my-12 on-primary--text d-flex align-center">
<div class="application-logo-container mr-3">
<img class="big" src="../../assets/logo.svg" alt="logo" />
</div>
<span class="application-name">
{{ applicationName }}
+2 -5
View File
@@ -4,10 +4,6 @@
<div class="toolbar d-flex justify-space-between white--text">
<div class="d-flex align-center">
<div class="application-logo-container mt-1">
<img
src="../../assets/logo.svg"
alt="logo"
/>
</div>
<div class="application-name ml-6 mr-12">
<template>
@@ -32,10 +28,11 @@
class="fill-height d-flex align-center justify-center px-3"
v-on="on"
>
<v-icon class="white--text mr-2">{{ item.icon }}</v-icon>
<div>{{ item.title }}</div>
<v-icon
v-if="item.dropdownOptions.length > 0"
class="white--text ml-3"
class="white--text ml-2"
>
mdi-chevron-down
</v-icon>
+35 -2
View File
@@ -1,13 +1,46 @@
<template>
<div id="status" class="ma-6">
<div class="d-flex justify-space-between">
<div class="right-header">
<h1>Status</h1>
</div>
<div class="left-header">
<v-btn @click="openCreateDialog()" color="primary"
>Status erstellen
</v-btn>
</div>
</div>
<status-table-component class="mt-3"/>
<create-status-component v-if="showCreateDialog" :showDialog="showCreateDialog"
@createdStatus="createdStatus" @closeDialog="showCreateDialog = false"/>
</div>
</template>
<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
@Component({})
@Component({
components: {
StatusTableComponent: () => import(
/* webpackChunkName: "StatusTableComponent" */
'@/components/status/StatusTable.component.vue'),
CreateStatusComponent: () => import(
/* webpackChunkName: "CreateStatusComponent" */
'@/components/status/CreateStatus.component.vue'
)
}
})
export default class StatusView extends Vue {
private showCreateDialog: boolean = false;
private openCreateDialog() {
this.showCreateDialog = true;
}
private createdStatus() {
this.showCreateDialog = false;
}
}
</script>
+38 -2
View File
@@ -1,13 +1,49 @@
<template>
<div id="users" class="ma-6">
<div class="d-flex justify-space-between">
<div class="right-header">
<h1>Benutzer</h1>
</div>
<div class="left-header">
<v-btn @click="openCreateDialog()" color="primary"
>Benutzer erstellen
</v-btn>
</div>
</div>
<user-table-component class="mt-3"/>
<create-user-component v-if="showCreateDialog" :showDialog="showCreateDialog"
@createdUser="createdUser" @closeDialog="showCreateDialog = false"/>
</div>
</template>
<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
import {namespace} from 'vuex-class';
@Component({})
const UserStore = namespace('user');
@Component({
components: {
UserTableComponent: () => import(
/* webpackChunkName: "UserTableComponent" */
'@/components/user/UserTable.component.vue'),
CreateUserComponent: () => import(
/* webpackChunkName: "CreateUserComponent" */
'@/components/user/CreateUser.component.vue'
)
}
})
export default class UsersView extends Vue {
private showCreateDialog: boolean = false;
private openCreateDialog() {
this.showCreateDialog = true;
}
private createdUser() {
this.showCreateDialog = false;
}
}
</script>