- Introduced "devpage" to show calculation dumps and change active user in "dev"-profile
This commit is contained in:
parent
10687ffe5d
commit
4c34a1bade
28 changed files with 1071 additions and 208 deletions
|
|
@ -1,12 +1,23 @@
|
||||||
import logger from "@/logger.js";
|
import logger from "@/logger.js";
|
||||||
import {useErrorStore} from "@/store/error.js";
|
import {useErrorStore} from "@/store/error.js";
|
||||||
|
|
||||||
|
const getCsrfToken = () => {
|
||||||
|
const value = `; ${document.cookie}`;
|
||||||
|
const parts = value.split(`; XSRF-TOKEN=`);
|
||||||
|
if (parts.length === 2) {
|
||||||
|
return decodeURIComponent(parts.pop().split(';').shift());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const performRequest = async (requestingStore, method, url, body, expectResponse = true, expectedException = null) => {
|
const performRequest = async (requestingStore, method, url, body, expectResponse = true, expectedException = null) => {
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
method: method,
|
method: method,
|
||||||
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'X-XSRF-TOKEN': getCsrfToken()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -27,6 +38,10 @@ const performDownload = async (url, toFile, expectResponse = true, expectedExcep
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'X-XSRF-TOKEN': getCsrfToken()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const request = {url: url, params: params, expectResponse: expectResponse, expectedException: expectedException, type: 'blob'};
|
const request = {url: url, params: params, expectResponse: expectResponse, expectedException: expectedException, type: 'blob'};
|
||||||
|
|
@ -56,6 +71,10 @@ const performUpload = async (url, file, expectResponse = true, expectedException
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'X-XSRF-TOKEN': getCsrfToken()
|
||||||
|
},
|
||||||
body: formData
|
body: formData
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ export default{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
.checkbox-container {
|
.checkbox-container {
|
||||||
|
|
@ -142,12 +141,17 @@ export default{
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
top: 1px;
|
top: 1px;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-item input:checked ~ .checkmark::after {
|
.checkbox-item input:checked ~ .checkmark::after {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-item:not(.disabled):hover input:checked ~ .checkmark::after {
|
||||||
|
border-color: #8DB3FE;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-label {
|
.checkbox-label {
|
||||||
color: #002F54;
|
color: #002F54;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
|
|
||||||
<box>
|
<box>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div>
|
<div class="button-container">
|
||||||
|
<basic-button variant="secondary" :show-icon="false" @click="$router.push({name: 'dev-page', state: {show: 'dumps'}})">Back</basic-button>
|
||||||
<basic-button variant="secondary" :show-icon="false" @click="expand = !expand">
|
<basic-button variant="secondary" :show-icon="false" @click="expand = !expand">
|
||||||
{{ expand === true ? 'Collapse all items' : 'Expand all items' }}
|
{{ expand === true ? 'Collapse all items' : 'Expand all items' }}
|
||||||
</basic-button>
|
</basic-button>
|
||||||
|
|
@ -34,7 +35,7 @@ export default {
|
||||||
components: {Box, BasicButton, ToggleSwitch, JsonTreeViewer},
|
components: {Box, BasicButton, ToggleSwitch, JsonTreeViewer},
|
||||||
async created() {
|
async created() {
|
||||||
|
|
||||||
const resp = await performRequest(null, "GET", `${config.backendUrl}/error/dump/${this.$route.params.id}`, null);
|
const resp = await performRequest(null, "GET", `${config.backendUrl}/dev/dump/${this.$route.params.id}`, null);
|
||||||
this.dump = resp.data;
|
this.dump = resp.data;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
@ -49,6 +50,12 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
|
||||||
|
<table-view :columns="columns" :data-source="fetch" :page-size="pageSize" :page="pagination.page" :page-count="pagination.pageCount" :total-count="pagination.totalCount" @row-click="showDump" :mouse-over="true"></table-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TableView from "@/components/UI/TableView.vue";
|
||||||
|
import performRequest from "@/backend.js";
|
||||||
|
import {config} from "@/config.js";
|
||||||
|
import Box from "@/components/UI/Box.vue";
|
||||||
|
import JsonTreeViewer from "@/components/UI/JsonTreeViewer.vue";
|
||||||
|
import BasicButton from "@/components/UI/BasicButton.vue";
|
||||||
|
import Modal from "@/components/UI/Modal.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CalculationDumpList",
|
||||||
|
components: {Modal, BasicButton, JsonTreeViewer, Box, TableView},
|
||||||
|
methods: {
|
||||||
|
showDump(dump) {
|
||||||
|
this.$router.push({name: 'dev-calculation-dump', params: {id: dump.id}});
|
||||||
|
},
|
||||||
|
async fetch(query) {
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (query?.searchTerm && query.searchTerm !== '')
|
||||||
|
params.append('filter', query.searchTerm);
|
||||||
|
if(query?.periodId)
|
||||||
|
params.append('valid', query.periodId);
|
||||||
|
|
||||||
|
if(query?.page)
|
||||||
|
params.append('page', query.page);
|
||||||
|
|
||||||
|
if(query?.pageSize)
|
||||||
|
params.append('limit', query.pageSize);
|
||||||
|
|
||||||
|
const resp = await performRequest(null, "GET", `${config.backendUrl}/dev/dump/${params.size === 0 ? '' : '?'}${params.toString()}`, null);
|
||||||
|
this.dump = resp.data;
|
||||||
|
this.pagination = { page: parseInt(resp.headers.get('X-Current-Page')), pageCount: parseInt(resp.headers.get('X-Page-Count')), totalCount: parseInt(resp.headers.get('X-Total-Count'))};
|
||||||
|
|
||||||
|
return this.dump;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
dump: null,
|
||||||
|
|
||||||
|
pageSize: 20,
|
||||||
|
pagination: {page: 1, pageCount: 1, totalCount: 1},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'id',
|
||||||
|
label: 'ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'calculation_date',
|
||||||
|
label: 'Calculation date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'job_state',
|
||||||
|
label: 'State',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'user_id',
|
||||||
|
label: 'User ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'premise.material.part_number',
|
||||||
|
label: 'Material',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'premise.supplier.name',
|
||||||
|
label: 'Supplier',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'premise.destinations',
|
||||||
|
label: 'Destinations',
|
||||||
|
formatter: (value) => {
|
||||||
|
return value.map(v => v.destination_node.name).join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
123
src/frontend/src/components/layout/dev/DevUserControl.vue
Normal file
123
src/frontend/src/components/layout/dev/DevUserControl.vue
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<box variant="border">
|
||||||
|
|
||||||
|
<div class="active-user"> Currently selected user: {{ activeUser }}</div>
|
||||||
|
</box>
|
||||||
|
|
||||||
|
|
||||||
|
<modal-dialog title="Do you want to change active user?" :state="showModal"
|
||||||
|
:message="`Do you want to set the active user to: ${userName}`" accept-text="Yes" dismiss-text="No"
|
||||||
|
@click="activateUser"></modal-dialog>
|
||||||
|
<table-view :columns="columns" :data-source="fetch" @row-click="selectUser" :mouse-over="true"></table-view>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import performRequest from "@/backend.js";
|
||||||
|
import {config} from "@/config.js";
|
||||||
|
import TableView from "@/components/UI/TableView.vue";
|
||||||
|
import ModalDialog from "@/components/UI/ModalDialog.vue";
|
||||||
|
import Box from "@/components/UI/Box.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DevUserControl",
|
||||||
|
components: {Box, ModalDialog, TableView},
|
||||||
|
created() {
|
||||||
|
this.getActiveUser();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetch() {
|
||||||
|
const resp = await performRequest(null, "GET", `${config.backendUrl}/dev/user`, null);
|
||||||
|
this.users = resp.data;
|
||||||
|
return this.users;
|
||||||
|
},
|
||||||
|
selectUser(user) {
|
||||||
|
this.selectedUser = user;
|
||||||
|
this.showModal = true;
|
||||||
|
|
||||||
|
},
|
||||||
|
async activateUser(action) {
|
||||||
|
if (action === "accept") {
|
||||||
|
await performRequest(null, "POST", `${config.backendUrl}/dev/user`, this.selectedUser, false);
|
||||||
|
await this.getActiveUser();
|
||||||
|
}
|
||||||
|
this.showModal = false;
|
||||||
|
},
|
||||||
|
async getActiveUser() {
|
||||||
|
const resp = await performRequest(null, "GET", `${config.backendUrl}/active-user`, null);
|
||||||
|
this.activeUserData = resp.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userName() {
|
||||||
|
return this.selectedUser?.firstname + " " + this.selectedUser?.lastname;
|
||||||
|
},
|
||||||
|
activeUser() {
|
||||||
|
return this.activeUserData?.firstname + " " + this.activeUserData?.lastname;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showModal: false,
|
||||||
|
activeUserData: null,
|
||||||
|
selectedUser: null,
|
||||||
|
users: null,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
key: 'firstname',
|
||||||
|
label: 'First name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lastname',
|
||||||
|
label: 'Last name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'mail',
|
||||||
|
label: 'E-Mail',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'workday_id',
|
||||||
|
label: 'Workday ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'groups',
|
||||||
|
label: 'User groups',
|
||||||
|
formatter: (value) => {
|
||||||
|
return value.join(", ")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'is_active',
|
||||||
|
label: 'active',
|
||||||
|
formatter: (value) => {
|
||||||
|
return value ? 'Yes' : 'No';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
page: 1,
|
||||||
|
pageCount: 1,
|
||||||
|
totalCount: 0
|
||||||
|
},
|
||||||
|
pageSize: 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.active-user {
|
||||||
|
font-size: 1.6rem
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -1,47 +1,46 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container" @focusout="focusLost">
|
<div class="container" :class="{ 'responsive': responsive }" @focusout="focusLost">
|
||||||
|
|
||||||
<div class="caption-column">Part number</div>
|
<div class="field-group">
|
||||||
|
<div class="caption-column">Part number</div>
|
||||||
<div class="input-column">
|
<div class="input-column">
|
||||||
|
<span>{{ partNumber }}</span>
|
||||||
<span>{{ partNumber }}</span>
|
<modal :state="modalSelectMaterial" @close="closeEditModal">
|
||||||
|
<select-material :part-number="partNumber" @close="modalEditClick"/>
|
||||||
<modal :state="modalSelectMaterial" @close="closeEditModal">
|
</modal>
|
||||||
<select-material :part-number="partNumber" @close="modalEditClick"/>
|
<icon-button icon="pencil-simple" @click="activateEditMode"></icon-button>
|
||||||
</modal>
|
|
||||||
<icon-button icon="pencil-simple" @click="activateEditMode"></icon-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="caption-column">Description</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="input-column">
|
|
||||||
<span>{{ description }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="caption-column">HS code</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="input-column">
|
|
||||||
<div class="hs-code-container">
|
|
||||||
<autosuggest-searchbar :activate-watcher="true" :fetch-suggestions="fetchHsCode" :initial-value="hsCode"
|
|
||||||
placeholder="Find hs code" no-results-text="Not found."></autosuggest-searchbar>
|
|
||||||
<icon-button icon="ArrowCounterClockwise"></icon-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-group">
|
||||||
|
<div class="caption-column">Description</div>
|
||||||
|
<div class="input-column">
|
||||||
|
<span>{{ description }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-group">
|
||||||
|
<div class="caption-column">HS code</div>
|
||||||
|
<div class="input-column">
|
||||||
|
<div class="hs-code-container">
|
||||||
|
<autosuggest-searchbar :activate-watcher="true" :fetch-suggestions="fetchHsCode" :initial-value="hsCode"
|
||||||
|
placeholder="Find hs code" no-results-text="Not found."></autosuggest-searchbar>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-group">
|
||||||
<div class="caption-column">Tariff rate [%]</div>
|
<div class="caption-column">Tariff rate [%]</div>
|
||||||
<div class="text-container input-field-tariffrate">
|
<div class="input-column">
|
||||||
<input ref="tariffRateInput" :value="tariffRatePercent" @blur="validateTariffRate"
|
<div class="text-container input-field-tariffrate">
|
||||||
class="input-field"
|
<input ref="tariffRateInput" :value="tariffRatePercent" @blur="validateTariffRate"
|
||||||
autocomplete="off"/>
|
class="input-field"
|
||||||
|
autocomplete="off"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -88,6 +87,10 @@ export default {
|
||||||
openSelectDirect: {
|
openSelectDirect: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
},
|
||||||
|
responsive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -164,16 +167,63 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.8rem;
|
gap: 0.8rem;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: auto 1fr;
|
||||||
grid-template-rows: repeat(3, fit-content(0));
|
grid-template-rows: repeat(4, fit-content(0));
|
||||||
gap: 1.6rem;
|
gap: 1.6rem;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Responsive Layout für Breiten unter 1500px - nur wenn responsive aktiviert ist */
|
||||||
|
@media (max-width: 1500px) {
|
||||||
|
.container.responsive {
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(auto, max-content) /* Spalte 1: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 2: Input */
|
||||||
|
minmax(auto, max-content) /* Spalte 3: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 4: Input/Dropdown */
|
||||||
|
minmax(auto, max-content) /* Spalte 5: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 6: Input */
|
||||||
|
minmax(auto, max-content) /* Spalte 7: Label */
|
||||||
|
minmax(120px, 1fr); /* Spalte 8: Input/Dropdown */
|
||||||
|
grid-template-rows: auto;
|
||||||
|
gap: 1.2rem 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .field-group {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .caption-column {
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .input-column {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .field-group {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .caption-column {
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .input-column {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
.input-column {
|
.input-column {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
@ -201,7 +251,6 @@ export default {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
padding: 0.6rem 1.2rem;
|
padding: 0.6rem 1.2rem;
|
||||||
/* box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);*/
|
|
||||||
border: 0.2rem solid #E3EDFF;
|
border: 0.2rem solid #E3EDFF;
|
||||||
transition: all 0.1s ease;
|
transition: all 0.1s ease;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
@ -210,7 +259,6 @@ export default {
|
||||||
.text-container:hover {
|
.text-container:hover {
|
||||||
background: #EEF4FF;
|
background: #EEF4FF;
|
||||||
border: 0.2rem solid #8DB3FE;
|
border: 0.2rem solid #8DB3FE;
|
||||||
/*transform: translateY(2px);*/
|
|
||||||
transform: scale(1.01);
|
transform: scale(1.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,8 +272,16 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-field-tariffrate {
|
.input-field-tariffrate {
|
||||||
min-width: 10rem;
|
min-width: 20rem;
|
||||||
flex: 0 1 10rem;
|
flex: 0 1 10rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Optimierung für kleinere Bildschirme unter 1500px - nur wenn responsive aktiviert ist */
|
||||||
|
@media (max-width: 1500px) {
|
||||||
|
.container.responsive .input-field-tariffrate {
|
||||||
|
min-width: auto;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container" @focusout="focusLost">
|
|
||||||
|
<div class="container" :class="{ 'responsive': responsive }" @focusout="focusLost">
|
||||||
<div class="caption-column">Length</div>
|
<div class="caption-column">Length</div>
|
||||||
<div class="input-column">
|
<div class="input-column">
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<input ref="lengthInput" :value="huLength" @blur="validateDimension('length', $event)" class="input-field"
|
<input ref="lengthInput" :value="huLength" @blur="validateDimension('length', $event)"
|
||||||
|
@keydown.enter="handleEnter('lengthInput', $event)" class="input-field"
|
||||||
autocomplete="off"/>
|
autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="caption-column">Dimension unit</div>
|
<div class="caption-column dimension-unit-label">Dimension unit</div>
|
||||||
<div class="input-column">
|
<div class="input-column dimension-unit-dropdown">
|
||||||
<dropdown :options="huDimensionUnits" v-model="huDimensionUnitSelected"></dropdown>
|
<dropdown :options="huDimensionUnits" v-model="huDimensionUnitSelected"></dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -16,7 +18,8 @@
|
||||||
<div class="caption-column">Width</div>
|
<div class="caption-column">Width</div>
|
||||||
<div class="input-column">
|
<div class="input-column">
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<input ref="widthInput" :value="huWidth" @blur="validateDimension('width', $event)" class="input-field"
|
<input ref="widthInput" :value="huWidth" @blur="validateDimension('width', $event)"
|
||||||
|
@keydown.enter="handleEnter('widthInput', $event)" class="input-field"
|
||||||
autocomplete="off"/>
|
autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -27,7 +30,8 @@
|
||||||
<div class="caption-column">Height</div>
|
<div class="caption-column">Height</div>
|
||||||
<div class="input-column">
|
<div class="input-column">
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<input ref="heightInput" :value="huHeight" @blur="validateDimension('height', $event)" class="input-field"
|
<input ref="heightInput" :value="huHeight" @blur="validateDimension('height', $event)"
|
||||||
|
@keydown.enter="handleEnter('heightInput', $event)" class="input-field"
|
||||||
autocomplete="off"/>
|
autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -38,7 +42,8 @@
|
||||||
<div class="caption-column">Weight</div>
|
<div class="caption-column">Weight</div>
|
||||||
<div class="input-column">
|
<div class="input-column">
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<input ref="weightInput" :value="huWeight" @blur="validateWeight('weight', $event)" class="input-field"
|
<input ref="weightInput" :value="huWeight" @blur="validateWeight('weight', $event)"
|
||||||
|
@keydown.enter="handleEnter('weightInput', $event)" class="input-field"
|
||||||
autocomplete="off"/>
|
autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -50,7 +55,8 @@
|
||||||
<div class="caption-column">Pieces per HU</div>
|
<div class="caption-column">Pieces per HU</div>
|
||||||
<div class="input-column">
|
<div class="input-column">
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<input ref="unitCountInput" :value="huUnitCount" @blur="validateCount" class="input-field"
|
<input ref="unitCountInput" :value="huUnitCount" @blur="validateCount"
|
||||||
|
@keydown.enter="handleEnter('unitCountInput', $event)" class="input-field"
|
||||||
autocomplete="off"/>
|
autocomplete="off"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -115,6 +121,10 @@ export default {
|
||||||
stackable: {
|
stackable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
|
},
|
||||||
|
responsive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -153,14 +163,14 @@ export default {
|
||||||
const unitType = this.huDimensionUnits.find(unit => unit.id === value)?.value;
|
const unitType = this.huDimensionUnits.find(unit => unit.id === value)?.value;
|
||||||
const decimals = (unitType === 'cm') ? 2 : ((unitType === 'm') ? 3 : 0);
|
const decimals = (unitType === 'cm') ? 2 : ((unitType === 'm') ? 3 : 0);
|
||||||
|
|
||||||
const parsedLength = (this.length ?? null) === null ? null : parseFloat(this.length.toFixed(decimals));
|
const parsedLength = (this.length ?? null) === null ? null : parseFloat(this.length.toFixed(decimals));
|
||||||
this.$emit('update:length', parsedLength);
|
this.$emit('update:length', parsedLength);
|
||||||
|
|
||||||
const parsedHeight = (this.height ?? null) === null ? null : parseFloat(this.height.toFixed(decimals));
|
const parsedHeight = (this.height ?? null) === null ? null : parseFloat(this.height.toFixed(decimals));
|
||||||
this.$emit('update:height', parsedHeight);
|
this.$emit('update:height', parsedHeight);
|
||||||
|
|
||||||
const parsedWidth = (this.width ?? null) === null ? null : parseFloat(this.width.toFixed(decimals));
|
const parsedWidth = (this.width ?? null) === null ? null : parseFloat(this.width.toFixed(decimals));
|
||||||
this.$emit('update:width', parsedWidth);
|
this.$emit('update:width', parsedWidth);
|
||||||
|
|
||||||
|
|
||||||
this.$emit('update:dimensionUnit', unitType);
|
this.$emit('update:dimensionUnit', unitType);
|
||||||
|
|
@ -187,6 +197,23 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleEnter(currentRef, event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Define the navigation order
|
||||||
|
const inputOrder = ['lengthInput', 'widthInput', 'heightInput', 'weightInput', 'unitCountInput'];
|
||||||
|
|
||||||
|
const currentIndex = inputOrder.indexOf(currentRef);
|
||||||
|
if (currentIndex !== -1 && currentIndex < inputOrder.length - 1) {
|
||||||
|
const nextRef = inputOrder[currentIndex + 1];
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs[nextRef]) {
|
||||||
|
this.$refs[nextRef].focus();
|
||||||
|
this.$refs[nextRef].select();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
focusLost(event) {
|
focusLost(event) {
|
||||||
if (!this.$el.contains(event.relatedTarget)) {
|
if (!this.$el.contains(event.relatedTarget)) {
|
||||||
this.$emit('save', 'packaging');
|
this.$emit('save', 'packaging');
|
||||||
|
|
@ -252,10 +279,127 @@ export default {
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.container {
|
.container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr auto auto;
|
grid-template-columns: auto auto auto auto;
|
||||||
grid-template-rows: repeat(5, fit-content(0));
|
grid-template-rows: repeat(5, fit-content(0));
|
||||||
gap: 1.2rem 1.6rem;
|
gap: 1.2rem 1.6rem;
|
||||||
flex: 1 1 auto;
|
flex: 0 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Layout für Breiten unter 1500px - nur wenn responsive aktiviert ist */
|
||||||
|
@media (max-width: 1500px) {
|
||||||
|
.container.responsive {
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(auto, max-content) /* Spalte 1: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 2: Input */
|
||||||
|
minmax(auto, max-content) /* Spalte 3: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 4: Input/Dropdown */
|
||||||
|
minmax(auto, max-content) /* Spalte 5: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 6: Input */
|
||||||
|
minmax(auto, max-content) /* Spalte 7: Label */
|
||||||
|
minmax(120px, 1fr); /* Spalte 8: Input/Dropdown */
|
||||||
|
grid-template-rows: auto;
|
||||||
|
gap: 1.2rem 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .field-group {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .caption-column {
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .input-column {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
/* First row: Length, Dimension unit, Weight, Weight unit */
|
||||||
|
.container.responsive > .caption-column:nth-child(1) { /* Length label */
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .input-column:nth-child(2) { /* Length input */
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .caption-column:nth-child(3) { /* Dimension unit label */
|
||||||
|
grid-column: 3;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .input-column:nth-child(4) { /* Dimension unit dropdown */
|
||||||
|
grid-column: 4;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .caption-column:nth-child(13) { /* Weight label */
|
||||||
|
grid-column: 5;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .input-column:nth-child(14) { /* Weight input */
|
||||||
|
grid-column: 6;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .caption-column:nth-child(15) { /* Weight unit label */
|
||||||
|
grid-column: 7;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .input-column:nth-child(16) { /* Weight unit dropdown */
|
||||||
|
grid-column: 8;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Second row: Width, Pieces per HU */
|
||||||
|
.container.responsive > .caption-column:nth-child(5) { /* Width label */
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .input-column:nth-child(6) { /* Width input */
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .caption-column:nth-child(17) { /* Pieces per HU label */
|
||||||
|
grid-column: 5;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .input-column:nth-child(18) { /* Pieces per HU input */
|
||||||
|
grid-column: 6;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Third row: Height, Checkboxes */
|
||||||
|
.container.responsive > .caption-column:nth-child(9) { /* Height label */
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive > .input-column:nth-child(10) { /* Height input */
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .input-column-chk {
|
||||||
|
grid-column: 7 / -1;
|
||||||
|
grid-row: 3;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide empty caption and input columns */
|
||||||
|
.container.responsive > .caption-column:nth-child(7),
|
||||||
|
.container.responsive > .input-column:nth-child(8),
|
||||||
|
.container.responsive > .caption-column:nth-child(11),
|
||||||
|
.container.responsive > .input-column:nth-child(12) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-field {
|
.input-field {
|
||||||
|
|
@ -268,7 +412,6 @@ export default {
|
||||||
color: #002F54;
|
color: #002F54;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 5rem;
|
min-width: 5rem;
|
||||||
max-width: 10rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-column-chk {
|
.input-column-chk {
|
||||||
|
|
@ -288,6 +431,7 @@ export default {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: #6b7280;
|
color: #6b7280;
|
||||||
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-container {
|
.text-container {
|
||||||
|
|
@ -296,17 +440,14 @@ export default {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
padding: 0.6rem 1.2rem;
|
padding: 0.6rem 1.2rem;
|
||||||
/* box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);*/
|
|
||||||
border: 0.2rem solid #E3EDFF;
|
border: 0.2rem solid #E3EDFF;
|
||||||
transition: all 0.1s ease;
|
transition: all 0.1s ease;
|
||||||
flex: 0 1 min(30rem, 100%);
|
flex: 1 1 auto
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-container:hover {
|
.text-container:hover {
|
||||||
background: #EEF4FF;
|
background: #EEF4FF;
|
||||||
border: 0.2rem solid #8DB3FE;
|
border: 0.2rem solid #8DB3FE;
|
||||||
/*transform: translateY(2px);*/
|
|
||||||
transform: scale(1.01);
|
transform: scale(1.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,33 @@
|
||||||
<template>
|
<template>
|
||||||
<div @focusout="focusLost">
|
<div @focusout="focusLost">
|
||||||
<div class="container">
|
<div class="container" :class="{ 'responsive': responsive }">
|
||||||
<div class="caption-column">MEK_A [EUR]</div>
|
<div class="field-group">
|
||||||
<div class="input-column">
|
<div class="caption-column">MEK_A [EUR]</div>
|
||||||
<div class="text-container">
|
<div class="input-column">
|
||||||
<input :value="priceFormatted" @blur="validatePrice" class="input-field"
|
<div class="text-container">
|
||||||
autocomplete="off"/>
|
<input :value="priceFormatted" @blur="validatePrice" class="input-field"
|
||||||
|
autocomplete="off"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="caption-column">Oversea share [%]</div>
|
|
||||||
<div class="input-column">
|
<div class="field-group">
|
||||||
<div class="text-container">
|
<div class="caption-column">Oversea share [%]</div>
|
||||||
<input :value="overSeaSharePercent" @blur="validateOverSeaShare" class="input-field"
|
<div class="input-column">
|
||||||
autocomplete="off"/>
|
<div class="text-container">
|
||||||
|
<input :value="overSeaSharePercent" @blur="validateOverSeaShare" class="input-field"
|
||||||
|
autocomplete="off"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="caption-column">Include FCA Fee</div>
|
|
||||||
<div class="input-column">
|
<div class="field-group">
|
||||||
<tooltip text="Select if a additional FCA has to be added during calculation">
|
<div class="caption-column">Include FCA Fee</div>
|
||||||
<checkbox :checked="includeFcaFee" @checkbox-changed="updateIncludeFcaFee"></checkbox>
|
<div class="input-column">
|
||||||
</tooltip>
|
<tooltip text="Select if a additional FCA has to be added during calculation">
|
||||||
|
<checkbox :checked="includeFcaFee" @checkbox-changed="updateIncludeFcaFee"></checkbox>
|
||||||
|
</tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -49,6 +57,10 @@ export default {
|
||||||
includeFcaFee: {
|
includeFcaFee: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
|
},
|
||||||
|
responsive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -109,7 +121,39 @@ export default {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Responsive Layout für Breiten unter 1500px - nur wenn responsive aktiviert ist */
|
||||||
|
@media (max-width: 1500px) {
|
||||||
|
.container.responsive {
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(auto, max-content) /* Spalte 1: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 2: Input */
|
||||||
|
minmax(auto, max-content) /* Spalte 3: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 4: Input/Dropdown */
|
||||||
|
minmax(auto, max-content) /* Spalte 5: Label */
|
||||||
|
minmax(120px, 1fr) /* Spalte 6: Input */
|
||||||
|
minmax(auto, max-content) /* Spalte 7: Label */
|
||||||
|
minmax(120px, 1fr); /* Spalte 8: Input/Dropdown */
|
||||||
|
grid-template-rows: auto;
|
||||||
|
gap: 1.2rem 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .field-group {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .caption-column {
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.responsive .input-column {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
.input-column {
|
.input-column {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -139,16 +183,14 @@ export default {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
padding: 0.6rem 1.2rem;
|
padding: 0.6rem 1.2rem;
|
||||||
/* box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);*/
|
|
||||||
border: 0.2rem solid #E3EDFF;
|
border: 0.2rem solid #E3EDFF;
|
||||||
transition: all 0.1s ease;
|
transition: all 0.1s ease;
|
||||||
flex: 1 1 fit-content(80rem);
|
flex: 1 1 auto
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-container:hover {
|
.text-container:hover {
|
||||||
background: #EEF4FF;
|
background: #EEF4FF;
|
||||||
border: 0.2rem solid #8DB3FE;
|
border: 0.2rem solid #8DB3FE;
|
||||||
/*transform: translateY(2px);*/
|
|
||||||
transform: scale(1.01);
|
transform: scale(1.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ export default {
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
gap: 1.6rem;
|
gap: 1.6rem;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
<div v-if="showRoutes" key="routes" class="destination-edit-cell-routes">
|
<div v-if="showRoutes" key="routes" class="destination-edit-cell-routes">
|
||||||
<destination-route v-for="route in destination.routes" :key="route.id" :route="route"
|
<destination-route v-for="route in destination.routes" :key="route.id" :route="route"
|
||||||
:selected="route.is_selected" @click="selectRoute(route.id)"></destination-route>
|
:selected="route.is_selected" @click="selectRoute(route.id)" :responsive="false"></destination-route>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="showRouteWarning">
|
<div v-else-if="showRouteWarning">
|
||||||
<div class="destination-edit-route-warning">
|
<div class="destination-edit-route-warning">
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@
|
||||||
<ph-train :size="18" v-else-if="isRail" class="destination-route-icon"></ph-train>
|
<ph-train :size="18" v-else-if="isRail" class="destination-route-icon"></ph-train>
|
||||||
<ph-truck :size="18" v-else-if="isRoad" class="destination-route-icon"></ph-truck>
|
<ph-truck :size="18" v-else-if="isRoad" class="destination-route-icon"></ph-truck>
|
||||||
<ph-navigation-arrow :size="18" v-else class="destination-route-icon"></ph-navigation-arrow>
|
<ph-navigation-arrow :size="18" v-else class="destination-route-icon"></ph-navigation-arrow>
|
||||||
<div><span v-for="element in routeElements" class="destination-route-element"> {{ element }} </span></div>
|
<div :class="{ 'route-details': true, 'no-responsive': !responsive }">
|
||||||
<basic-badge v-if="cheapest" variant="secondary">CHEAPEST</basic-badge>
|
<span v-for="element in routeElements" class="destination-route-element"> {{ element }} </span>
|
||||||
<basic-badge v-if="fastest" variant="primary">FASTEST</basic-badge>
|
</div>
|
||||||
|
<!-- <basic-badge v-if="cheapest" variant="secondary">CHEAPEST</basic-badge>-->
|
||||||
|
<!-- <basic-badge v-if="fastest" variant="primary">FASTEST</basic-badge>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -44,6 +46,11 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
responsive: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -108,7 +115,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.destination-route-inner-container--selected {
|
.destination-route-inner-container--selected {
|
||||||
background: #ffffff;
|
background: #EEF4FF;
|
||||||
border: 0.2rem solid #8DB3FE;
|
border: 0.2rem solid #8DB3FE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +147,7 @@ export default {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.destination-route-inner-container > div {
|
.destination-route-inner-container > div:not(.no-responsive) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,4 +156,13 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When responsive is disabled, always show content */
|
||||||
|
.no-responsive {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-responsive ~ .destination-route-icon {
|
||||||
|
margin-right: 0.8rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -86,6 +86,7 @@
|
||||||
v-model:stackable="componentProps.stackable"
|
v-model:stackable="componentProps.stackable"
|
||||||
v-model:preSelectedNode="componentProps.preSelectedNode"
|
v-model:preSelectedNode="componentProps.preSelectedNode"
|
||||||
v-model:openSelectDirect="componentProps.openSelectDirect"
|
v-model:openSelectDirect="componentProps.openSelectDirect"
|
||||||
|
:responsive="false"
|
||||||
@update-material="updateMaterial"
|
@update-material="updateMaterial"
|
||||||
@update-supplier="updateSupplier"
|
@update-supplier="updateSupplier"
|
||||||
@close="closeEditModalAction('cancel')"
|
@close="closeEditModalAction('cancel')"
|
||||||
|
|
@ -318,7 +319,7 @@ export default {
|
||||||
if (id === -1) {
|
if (id === -1) {
|
||||||
// clear
|
// clear
|
||||||
this.componentsData = {
|
this.componentsData = {
|
||||||
price: {props: {price: 0, overSeaShare: 0.0, includeFcaFee: true}},
|
price: {props: {price: 0, overSeaShare: 0.0, includeFcaFee: false}},
|
||||||
material: {props: {partNumber: "", hsCode: "", tariffRate: 0.00, description: "", openSelectDirect: true}},
|
material: {props: {partNumber: "", hsCode: "", tariffRate: 0.00, description: "", openSelectDirect: true}},
|
||||||
packaging: {
|
packaging: {
|
||||||
props: {
|
props: {
|
||||||
|
|
|
||||||
|
|
@ -28,28 +28,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|
||||||
<h3 class="sub-header">Master data</h3>
|
<h3 class="sub-header">Supplier</h3>
|
||||||
<div class="master-data-container">
|
<div class="supplier-container">
|
||||||
<box class="master-data-item master-data-stretched-item">
|
<box class="supplier-item">
|
||||||
<supplier-view :supplier-address="premise.supplier.address"
|
<supplier-view :supplier-address="premise.supplier.address"
|
||||||
:supplier-name="premise.supplier.name"
|
:supplier-name="premise.supplier.name"
|
||||||
:supplier-coordinates="premise.supplier.location"
|
:supplier-coordinates="premise.supplier.location"
|
||||||
:iso-code="premise.supplier.country.iso_code"
|
:iso-code="premise.supplier.country.iso_code"
|
||||||
@update-supplier="updateSupplier"
|
@update-supplier="updateSupplier"
|
||||||
></supplier-view>
|
></supplier-view>
|
||||||
</box>
|
|
||||||
<box class="master-data-item master-data-stretched-item master-data-packaging">
|
|
||||||
<packaging-edit v-model:length="premise.handling_unit.length"
|
|
||||||
v-model:width="premise.handling_unit.width"
|
|
||||||
v-model:height="premise.handling_unit.height"
|
|
||||||
v-model:weight="premise.handling_unit.weight"
|
|
||||||
v-model:weight-unit="premise.handling_unit.weight_unit"
|
|
||||||
v-model:dimension-unit="premise.handling_unit.dimension_unit"
|
|
||||||
v-model:unit-count="premise.handling_unit.content_unit_count"
|
|
||||||
v-model:stackable="premise.is_stackable"
|
|
||||||
v-model:mixable="premise.is_mixable"
|
|
||||||
@save="save"></packaging-edit>
|
|
||||||
</box>
|
</box>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="sub-header">Master data</h3>
|
||||||
|
<div class="master-data-container">
|
||||||
<box class="master-data-item">
|
<box class="master-data-item">
|
||||||
<material-edit :part-number="premise.material.part_number"
|
<material-edit :part-number="premise.material.part_number"
|
||||||
:description="premise.material.name"
|
:description="premise.material.name"
|
||||||
|
|
@ -65,7 +57,18 @@
|
||||||
v-model:price="premise.material_cost"
|
v-model:price="premise.material_cost"
|
||||||
@save="save"></price-edit>
|
@save="save"></price-edit>
|
||||||
</box>
|
</box>
|
||||||
|
<box class="master-data-item">
|
||||||
|
<packaging-edit v-model:length="premise.handling_unit.length"
|
||||||
|
v-model:width="premise.handling_unit.width"
|
||||||
|
v-model:height="premise.handling_unit.height"
|
||||||
|
v-model:weight="premise.handling_unit.weight"
|
||||||
|
v-model:weight-unit="premise.handling_unit.weight_unit"
|
||||||
|
v-model:dimension-unit="premise.handling_unit.dimension_unit"
|
||||||
|
v-model:unit-count="premise.handling_unit.content_unit_count"
|
||||||
|
v-model:stackable="premise.is_stackable"
|
||||||
|
v-model:mixable="premise.is_mixable"
|
||||||
|
@save="save"></packaging-edit>
|
||||||
|
</box>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="sub-header">Destinations & routes</h3>
|
<h3 class="sub-header">Destinations & routes</h3>
|
||||||
|
|
@ -226,41 +229,29 @@ export default {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.supplier-container {
|
||||||
|
margin: 2.4rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supplier-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.master-data-container {
|
.master-data-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 4fr 4fr 5fr;
|
||||||
grid-template-rows: auto auto;
|
|
||||||
gap: 1.6rem;
|
gap: 1.6rem;
|
||||||
margin: 2.4rem 0;
|
margin: 2.4rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.master-data-stretched-item {
|
.master-data-item {
|
||||||
grid-row: span 2 / span 2;
|
padding: 2.4rem;
|
||||||
}
|
|
||||||
|
|
||||||
.master-data-packaging {
|
|
||||||
grid-column-start: 3;
|
|
||||||
grid-row-start: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive layout for viewports below 1500px */
|
/* Responsive layout for viewports below 1500px */
|
||||||
@media (max-width: 1500px) {
|
@media (max-width: 1500px) {
|
||||||
.master-data-container {
|
.master-data-container {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-template-rows: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.master-data-stretched-item {
|
|
||||||
grid-row: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.master-data-packaging {
|
|
||||||
grid-column-start: auto;
|
|
||||||
grid-row-start: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.master-data-item {
|
|
||||||
padding: 2.4rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
65
src/frontend/src/pages/DevPage.vue
Normal file
65
src/frontend/src/pages/DevPage.vue
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<div class="edit-calculation-container">
|
||||||
|
<div class="header-container">
|
||||||
|
<h2 class="page-header">DevPage</h2>
|
||||||
|
<div>
|
||||||
|
<box class="box-container">
|
||||||
|
<tab-container ref="refTab" :tabs="tabsConfig" class="tab-container" @tab-changed="handleTabChange">
|
||||||
|
</tab-container>
|
||||||
|
</box>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Box from "@/components/UI/Box.vue";
|
||||||
|
import TabContainer from "@/components/UI/TabContainer.vue";
|
||||||
|
import {markRaw} from "vue";
|
||||||
|
import CalculationDumpList from "@/components/layout/dev/CalculationDumpList.vue";
|
||||||
|
import DevUserControl from "@/components/layout/dev/DevUserControl.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DevPage",
|
||||||
|
components: {TabContainer, Box},
|
||||||
|
mounted() {
|
||||||
|
if(history.state.show === 'dumps') {
|
||||||
|
this.$refs.refTab.setActiveTab(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTab: null,
|
||||||
|
tabsConfig: [
|
||||||
|
{
|
||||||
|
title: 'User control',
|
||||||
|
component: markRaw(DevUserControl),
|
||||||
|
props: {isSelected: false},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Calculation dump',
|
||||||
|
component: markRaw(CalculationDumpList),
|
||||||
|
props: {isSelected: false},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleTabChange(eventData) {
|
||||||
|
|
||||||
|
console.log("handleTabChange")
|
||||||
|
|
||||||
|
const { index, tab } = eventData;
|
||||||
|
console.log(`Tab ${index} activated:`, tab.title);
|
||||||
|
|
||||||
|
this.tabsConfig.forEach(t => t.props.isSelected = t.title === tab.title);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -6,7 +6,8 @@ import CalculationSingleEdit from "@/pages/CalculationSingleEdit.vue";
|
||||||
import CalculationMassEdit from "@/pages/CalculationMassEdit.vue";
|
import CalculationMassEdit from "@/pages/CalculationMassEdit.vue";
|
||||||
import CalculationAssistant from "@/pages/CalculationAssistant.vue";
|
import CalculationAssistant from "@/pages/CalculationAssistant.vue";
|
||||||
import ErrorLog from "@/pages/ErrorLog.vue";
|
import ErrorLog from "@/pages/ErrorLog.vue";
|
||||||
import CalcualtionDump from "@/pages/CalcualtionDump.vue";
|
import CalculationDump from "@/components/layout/dev/CalculationDump.vue";
|
||||||
|
import DevPage from "@/pages/DevPage.vue";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
|
|
@ -56,10 +57,16 @@ const router = createRouter({
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/error/dump/:id',
|
path: '/dev/dump/:id',
|
||||||
component: CalcualtionDump
|
component: CalculationDump,
|
||||||
|
name: 'dev-calculation-dump'
|
||||||
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/dev',
|
||||||
|
component: DevPage,
|
||||||
|
name: 'dev-page',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
redirect: '/calculations'
|
redirect: '/calculations'
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public class CorsConfig implements WebMvcConfigurer {
|
||||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||||
.allowedHeaders("*")
|
.allowedHeaders("*")
|
||||||
.exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
|
.exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
|
||||||
.allowCredentials(false);
|
.allowCredentials(true);
|
||||||
} else {
|
} else {
|
||||||
// Production CORS configuration
|
// Production CORS configuration
|
||||||
registry.addMapping("/api/**")
|
registry.addMapping("/api/**")
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,34 @@ package de.avatic.lcc.config;
|
||||||
import de.avatic.lcc.model.users.User;
|
import de.avatic.lcc.model.users.User;
|
||||||
import de.avatic.lcc.repositories.users.GroupRepository;
|
import de.avatic.lcc.repositories.users.GroupRepository;
|
||||||
import de.avatic.lcc.repositories.users.UserRepository;
|
import de.avatic.lcc.repositories.users.UserRepository;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
||||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||||
|
import org.springframework.security.web.csrf.CsrfToken;
|
||||||
|
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||||
|
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
@ -28,11 +40,18 @@ public class SecurityConfig {
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/api/**").authenticated()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.oauth2Login(oauth2 -> oauth2
|
.oauth2Login(oauth2 -> oauth2
|
||||||
.defaultSuccessUrl("/", true)
|
.defaultSuccessUrl("/", true)
|
||||||
);
|
|
||||||
|
)
|
||||||
|
.csrf(csrf -> csrf
|
||||||
|
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
||||||
|
.csrfTokenRequestHandler(new LccCsrfTokenRequestHandler())
|
||||||
|
)
|
||||||
|
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class);
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +61,11 @@ public class SecurityConfig {
|
||||||
public SecurityFilterChain devSecurityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain devSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||||
return http
|
return http
|
||||||
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
|
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(csrf -> csrf
|
||||||
|
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
||||||
|
.csrfTokenRequestHandler(new LccCsrfTokenRequestHandler())
|
||||||
|
)
|
||||||
|
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +78,7 @@ public class SecurityConfig {
|
||||||
OidcUser oidcUser = delegate.loadUser(userRequest);
|
OidcUser oidcUser = delegate.loadUser(userRequest);
|
||||||
Integer userId = null;
|
Integer userId = null;
|
||||||
|
|
||||||
// // Debug: Print all claims
|
// Debug: Print all claims
|
||||||
// System.out.println("=== ID Token Claims ===");
|
// System.out.println("=== ID Token Claims ===");
|
||||||
// oidcUser.getIdToken().getClaims().forEach((key, value) ->
|
// oidcUser.getIdToken().getClaims().forEach((key, value) ->
|
||||||
// System.out.println(key + ": " + value)
|
// System.out.println(key + ": " + value)
|
||||||
|
|
@ -64,6 +87,15 @@ public class SecurityConfig {
|
||||||
|
|
||||||
Set<GrantedAuthority> mappedAuthorities = new HashSet<>(oidcUser.getAuthorities());
|
Set<GrantedAuthority> mappedAuthorities = new HashSet<>(oidcUser.getAuthorities());
|
||||||
|
|
||||||
|
String workdayId = oidcUser.getAttribute("workday_id");
|
||||||
|
if (workdayId != null) {
|
||||||
|
User user = userRepository.getByWorkdayId(workdayId);
|
||||||
|
if (user != null) {
|
||||||
|
user.getGroups().forEach(group -> mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + group.getName())));
|
||||||
|
userId = user.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try different ways to get email
|
// Try different ways to get email
|
||||||
String email = oidcUser.getEmail();
|
String email = oidcUser.getEmail();
|
||||||
if (email == null) {
|
if (email == null) {
|
||||||
|
|
@ -79,10 +111,10 @@ public class SecurityConfig {
|
||||||
if (email != null) {
|
if (email != null) {
|
||||||
User user = userRepository.getByEmail(email);
|
User user = userRepository.getByEmail(email);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
user.getGroups().forEach(group -> mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + group.getName())));
|
user.getGroups().forEach(group -> mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + group.getName().toUpperCase())));
|
||||||
userId = user.getId();
|
userId = user.getId();
|
||||||
} else {
|
} else {
|
||||||
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_default"));
|
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_BASIC"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,4 +127,35 @@ public class SecurityConfig {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class LccCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
|
||||||
|
private final CsrfTokenRequestHandler delegate = new CsrfTokenRequestAttributeHandler();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
Supplier<CsrfToken> csrfToken) {
|
||||||
|
this.delegate.handle(request, response, csrfToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
|
||||||
|
if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
|
||||||
|
return super.resolveCsrfTokenValue(request, csrfToken);
|
||||||
|
}
|
||||||
|
return this.delegate.resolveCsrfTokenValue(request, csrfToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class CsrfCookieFilter extends OncePerRequestFilter {
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response,
|
||||||
|
@NotNull FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
|
||||||
|
if (csrfToken != null) {
|
||||||
|
csrfToken.getToken();
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,9 @@ import java.util.Optional;
|
||||||
public class ErrorController {
|
public class ErrorController {
|
||||||
|
|
||||||
private final SysErrorService sysErrorService;
|
private final SysErrorService sysErrorService;
|
||||||
private final DumpRepository dumpRepository;
|
|
||||||
|
|
||||||
public ErrorController(SysErrorService sysErrorService, DumpRepository dumpRepository) {
|
public ErrorController(SysErrorService sysErrorService) {
|
||||||
this.sysErrorService = sysErrorService;
|
this.sysErrorService = sysErrorService;
|
||||||
this.dumpRepository = dumpRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
|
@ -43,9 +41,6 @@ public class ErrorController {
|
||||||
.body(errors.toList());
|
.body(errors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping({"/dump/{id}", "/dump/{id}/"})
|
|
||||||
public ResponseEntity<CalculationJobDumpDTO> getDump(@PathVariable Integer id) {
|
|
||||||
return ResponseEntity.ok(dumpRepository.getDump(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package de.avatic.lcc.controller.dev;
|
||||||
|
|
||||||
|
import com.azure.core.annotation.BodyParam;
|
||||||
|
import de.avatic.lcc.dto.error.CalculationJobDumpDTO;
|
||||||
|
import de.avatic.lcc.dto.users.UserDTO;
|
||||||
|
import de.avatic.lcc.repositories.error.DumpRepository;
|
||||||
|
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
|
||||||
|
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
|
||||||
|
import de.avatic.lcc.repositories.users.UserRepository;
|
||||||
|
import de.avatic.lcc.service.transformer.users.UserTransformer;
|
||||||
|
import de.avatic.lcc.service.users.authorization.AuthorizationService;
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@Profile("dev")
|
||||||
|
@RequestMapping({"/api/dev", "/api/dev/"})
|
||||||
|
public class DevController {
|
||||||
|
|
||||||
|
private final AuthorizationService authorizationService;
|
||||||
|
private final DumpRepository dumpRepository;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final UserTransformer userTransformer;
|
||||||
|
|
||||||
|
public DevController(AuthorizationService authorizationService, DumpRepository dumpRepository, UserRepository userRepository, UserTransformer userTransformer) {
|
||||||
|
this.authorizationService = authorizationService;
|
||||||
|
this.dumpRepository = dumpRepository;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.userTransformer = userTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping({"/dump/{id}", "/dump/{id}/"})
|
||||||
|
public ResponseEntity<CalculationJobDumpDTO> getDump(@PathVariable Integer id) {
|
||||||
|
return ResponseEntity.ok(dumpRepository.getDump(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping({"/dump/", "/dump"})
|
||||||
|
public ResponseEntity<List<CalculationJobDumpDTO>> listDumps(
|
||||||
|
@RequestParam(defaultValue = "20") @Min(1) int limit,
|
||||||
|
@RequestParam(defaultValue = "1") @Min(1) int page) {
|
||||||
|
|
||||||
|
var dump = dumpRepository.listDumps(new SearchQueryPagination(page, limit));
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header("X-Total-Count", String.valueOf(dump.getTotalElements()))
|
||||||
|
.header("X-Page-Count", String.valueOf(dump.getTotalPages()))
|
||||||
|
.header("X-Current-Page", String.valueOf(page))
|
||||||
|
.body(dump.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping({"/user"})
|
||||||
|
public ResponseEntity<List<UserDTO>> listUser(@RequestParam(defaultValue = "20") @Min(1) int limit,
|
||||||
|
@RequestParam(defaultValue = "1") @Min(1) int page) {
|
||||||
|
|
||||||
|
var users = SearchQueryResult.map(userRepository.listUsers(new SearchQueryPagination(page, limit)), userTransformer::toUserDTO);
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header("X-Total-Count", String.valueOf(users.getTotalElements()))
|
||||||
|
.header("X-Page-Count", String.valueOf(users.getTotalPages()))
|
||||||
|
.header("X-Current-Page", String.valueOf(page))
|
||||||
|
.body(users.toList());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping({"/user", "/user/"})
|
||||||
|
public ResponseEntity<Void> setActiveUser(@RequestBody UserDTO user) {
|
||||||
|
authorizationService.setActiveUser(user.getEmail());
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package de.avatic.lcc.controller.users;
|
||||||
|
|
||||||
|
import de.avatic.lcc.dto.users.UserDTO;
|
||||||
|
import de.avatic.lcc.service.transformer.users.UserTransformer;
|
||||||
|
import de.avatic.lcc.service.users.authorization.AuthorizationService;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping({"/api/active-user", "/api/active-user/"})
|
||||||
|
public class ActiveUserController {
|
||||||
|
|
||||||
|
|
||||||
|
private final AuthorizationService authorizationService;
|
||||||
|
private final UserTransformer userTransformer;
|
||||||
|
|
||||||
|
public ActiveUserController(AuthorizationService authorizationService, UserTransformer userTransformer) {
|
||||||
|
this.authorizationService = authorizationService;
|
||||||
|
this.userTransformer = userTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<UserDTO> getActiveUser() {
|
||||||
|
return ResponseEntity.ok(userTransformer.toUserDTO(authorizationService.getActiveUser()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -6,9 +6,12 @@ import de.avatic.lcc.dto.error.CalculationJobRouteSectionDumpDTO;
|
||||||
import de.avatic.lcc.dto.error.ErrorLogDTO;
|
import de.avatic.lcc.dto.error.ErrorLogDTO;
|
||||||
import de.avatic.lcc.dto.error.ErrorLogTraceItemDto;
|
import de.avatic.lcc.dto.error.ErrorLogTraceItemDto;
|
||||||
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
|
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
|
||||||
|
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
|
||||||
|
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
|
||||||
import de.avatic.lcc.repositories.premise.PremiseRepository;
|
import de.avatic.lcc.repositories.premise.PremiseRepository;
|
||||||
import de.avatic.lcc.service.transformer.premise.PremiseTransformer;
|
import de.avatic.lcc.service.transformer.premise.PremiseTransformer;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
|
@ -52,29 +55,7 @@ public class DumpRepository {
|
||||||
CalculationJobDumpDTO dump = namedParameterJdbcTemplate.queryForObject(
|
CalculationJobDumpDTO dump = namedParameterJdbcTemplate.queryForObject(
|
||||||
calculationJobQuery,
|
calculationJobQuery,
|
||||||
params,
|
params,
|
||||||
(rs, rowNum) -> {
|
new CalculationMapper());
|
||||||
CalculationJobDumpDTO dto = new CalculationJobDumpDTO();
|
|
||||||
dto.setId(rs.getInt("id"));
|
|
||||||
dto.setPremiseId(rs.getInt("premise_id"));
|
|
||||||
|
|
||||||
Timestamp calculationDate = rs.getTimestamp("calculation_date");
|
|
||||||
if (calculationDate != null) {
|
|
||||||
dto.setCalculationDate(calculationDate.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
dto.setValidityPeriodId(rs.getInt("validity_period_id"));
|
|
||||||
dto.setPropertySetId(rs.getInt("property_set_id"));
|
|
||||||
dto.setJobState(rs.getString("job_state"));
|
|
||||||
dto.setUserId(rs.getInt("user_id"));
|
|
||||||
|
|
||||||
// Check if there's an error_id
|
|
||||||
Integer errorId = rs.getObject("error_id", Integer.class);
|
|
||||||
if (errorId != null) {
|
|
||||||
dto.setErrorLog(loadErrorLog(errorId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return dto;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load premise details
|
// Load premise details
|
||||||
dump.setPremise(loadPremiseDetails(dump.getPremiseId()));
|
dump.setPremise(loadPremiseDetails(dump.getPremiseId()));
|
||||||
|
|
@ -229,28 +210,7 @@ public class DumpRepository {
|
||||||
|
|
||||||
CalculationJobDumpDTO dump = jdbcTemplate.queryForObject(
|
CalculationJobDumpDTO dump = jdbcTemplate.queryForObject(
|
||||||
calculationJobQuery,
|
calculationJobQuery,
|
||||||
(rs, rowNum) -> {
|
new CalculationMapper(),
|
||||||
CalculationJobDumpDTO dto = new CalculationJobDumpDTO();
|
|
||||||
dto.setId(rs.getInt("id"));
|
|
||||||
dto.setPremiseId(rs.getInt("premise_id"));
|
|
||||||
|
|
||||||
Timestamp calculationDate = rs.getTimestamp("calculation_date");
|
|
||||||
if (calculationDate != null) {
|
|
||||||
dto.setCalculationDate(calculationDate.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
dto.setValidityPeriodId(rs.getInt("validity_period_id"));
|
|
||||||
dto.setPropertySetId(rs.getInt("property_set_id"));
|
|
||||||
dto.setJobState(rs.getString("job_state"));
|
|
||||||
dto.setUserId(rs.getInt("user_id"));
|
|
||||||
|
|
||||||
Integer errorId = rs.getObject("error_id", Integer.class);
|
|
||||||
if (errorId != null) {
|
|
||||||
dto.setErrorLog(loadErrorLogAlternative(errorId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return dto;
|
|
||||||
},
|
|
||||||
id // Parameter passed directly without Object array
|
id // Parameter passed directly without Object array
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -307,6 +267,92 @@ public class DumpRepository {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SearchQueryResult<CalculationJobDumpDTO> listDumps(SearchQueryPagination searchQueryPagination) {
|
||||||
|
|
||||||
|
String calculationJobQuery = """
|
||||||
|
SELECT cj.id, cj.premise_id, cj.calculation_date, cj.validity_period_id,
|
||||||
|
cj.property_set_id, cj.job_state, cj.error_id, cj.user_id
|
||||||
|
FROM calculation_job cj
|
||||||
|
ORDER BY id DESC LIMIT :limit OFFSET :offset
|
||||||
|
""";
|
||||||
|
|
||||||
|
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||||
|
params.addValue("offset", searchQueryPagination.getOffset());
|
||||||
|
params.addValue("limit", searchQueryPagination.getLimit());
|
||||||
|
|
||||||
|
var dumps = namedParameterJdbcTemplate.query(
|
||||||
|
calculationJobQuery,
|
||||||
|
params,
|
||||||
|
(rs, _) -> {
|
||||||
|
CalculationJobDumpDTO dto = new CalculationJobDumpDTO();
|
||||||
|
dto.setId(rs.getInt("id"));
|
||||||
|
dto.setPremiseId(rs.getInt("premise_id"));
|
||||||
|
|
||||||
|
Timestamp calculationDate = rs.getTimestamp("calculation_date");
|
||||||
|
if (calculationDate != null) {
|
||||||
|
dto.setCalculationDate(calculationDate.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.setValidityPeriodId(rs.getInt("validity_period_id"));
|
||||||
|
dto.setPropertySetId(rs.getInt("property_set_id"));
|
||||||
|
dto.setJobState(rs.getString("job_state"));
|
||||||
|
dto.setUserId(rs.getInt("user_id"));
|
||||||
|
|
||||||
|
// Check if there's an error_id
|
||||||
|
Integer errorId = rs.getObject("error_id", Integer.class);
|
||||||
|
if (errorId != null) {
|
||||||
|
dto.setErrorLog(loadErrorLog(errorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
});
|
||||||
|
|
||||||
|
for(var dump : dumps) {
|
||||||
|
// Load premise details
|
||||||
|
dump.setPremise(loadPremiseDetails(dump.getPremiseId()));
|
||||||
|
|
||||||
|
// Load destinations
|
||||||
|
dump.setDestinations(loadCalculationJobDestinations(dump.getId()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SearchQueryResult<>(dumps, searchQueryPagination.getPage(),countCalculations(), searchQueryPagination.getLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CalculationMapper implements RowMapper<CalculationJobDumpDTO> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CalculationJobDumpDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||||
|
|
||||||
|
CalculationJobDumpDTO dto = new CalculationJobDumpDTO();
|
||||||
|
dto.setId(rs.getInt("id"));
|
||||||
|
dto.setPremiseId(rs.getInt("premise_id"));
|
||||||
|
|
||||||
|
Timestamp calculationDate = rs.getTimestamp("calculation_date");
|
||||||
|
if (calculationDate != null) {
|
||||||
|
dto.setCalculationDate(calculationDate.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.setValidityPeriodId(rs.getInt("validity_period_id"));
|
||||||
|
dto.setPropertySetId(rs.getInt("property_set_id"));
|
||||||
|
dto.setJobState(rs.getString("job_state"));
|
||||||
|
dto.setUserId(rs.getInt("user_id"));
|
||||||
|
|
||||||
|
// Check if there's an error_id
|
||||||
|
Integer errorId = rs.getObject("error_id", Integer.class);
|
||||||
|
if (errorId != null) {
|
||||||
|
dto.setErrorLog(loadErrorLog(errorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer countCalculations() {
|
||||||
|
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM calculation_job", Integer.class);
|
||||||
|
}
|
||||||
|
|
||||||
private static class CalculationJobDestinationRowMapper implements RowMapper<CalculationJobDestinationDumpDTO> {
|
private static class CalculationJobDestinationRowMapper implements RowMapper<CalculationJobDestinationDumpDTO> {
|
||||||
@Override
|
@Override
|
||||||
public CalculationJobDestinationDumpDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
|
public CalculationJobDestinationDumpDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ public class UserRepository {
|
||||||
@Transactional
|
@Transactional
|
||||||
public void update(User user) {
|
public void update(User user) {
|
||||||
|
|
||||||
Integer userId = findUserId(user.getWorkdayId());
|
Integer userId = getUserIdByWorkdayId(user.getWorkdayId());
|
||||||
|
|
||||||
List<Integer> groupIds = findGroupIds(user.getGroups().stream().map(Group::getName).toList());
|
List<Integer> groupIds = findGroupIds(user.getGroups().stream().map(Group::getName).toList());
|
||||||
|
|
||||||
|
|
@ -141,7 +141,8 @@ public class UserRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Integer findUserId(String workdayId) {
|
@Transactional
|
||||||
|
public Integer getUserIdByWorkdayId(String workdayId) {
|
||||||
List<Integer> results = jdbcTemplate.query("SELECT id FROM sys_user WHERE workday_id = ?",
|
List<Integer> results = jdbcTemplate.query("SELECT id FROM sys_user WHERE workday_id = ?",
|
||||||
(rs, rowNum) -> rs.getInt("id"),
|
(rs, rowNum) -> rs.getInt("id"),
|
||||||
workdayId);
|
workdayId);
|
||||||
|
|
@ -149,6 +150,15 @@ public class UserRepository {
|
||||||
return results.isEmpty() ? null : results.getFirst();
|
return results.isEmpty() ? null : results.getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public User getByWorkdayId(String workdayId) {
|
||||||
|
List<User> results = jdbcTemplate.query("SELECT id FROM sys_user WHERE workday_id = ?",
|
||||||
|
new UserMapper(),
|
||||||
|
workdayId);
|
||||||
|
|
||||||
|
return results.isEmpty() ? null : results.getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public User getById(Integer id) {
|
public User getById(Integer id) {
|
||||||
String query = """
|
String query = """
|
||||||
|
|
|
||||||
|
|
@ -88,12 +88,6 @@ public class PremisesService {
|
||||||
//TODO use actual user.
|
//TODO use actual user.
|
||||||
userId = 1;
|
userId = 1;
|
||||||
|
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
||||||
|
|
||||||
//todo make a service. and simulate user rights in dev profile.
|
|
||||||
if (authentication != null && authentication.getPrincipal() instanceof LccOidcUser) {
|
|
||||||
LccOidcUser oidcUser = (LccOidcUser) authentication.getPrincipal();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return SearchQueryResult.map(premiseRepository.listPremises(filter, new SearchQueryPagination(page, limit), userId, deleted, archived, done), admin ? premiseTransformer::toPremiseDTOWithUserInfo : premiseTransformer::toPremiseDTO);
|
return SearchQueryResult.map(premiseRepository.listPremises(filter, new SearchQueryPagination(page, limit), userId, deleted, archived, done), admin ? premiseTransformer::toPremiseDTOWithUserInfo : premiseTransformer::toPremiseDTO);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
package de.avatic.lcc.service.users;
|
package de.avatic.lcc.service.users;
|
||||||
|
|
||||||
|
import de.avatic.lcc.config.LccOidcUser;
|
||||||
import de.avatic.lcc.dto.users.UserDTO;
|
import de.avatic.lcc.dto.users.UserDTO;
|
||||||
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
|
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
|
||||||
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
|
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
|
||||||
import de.avatic.lcc.repositories.users.UserRepository;
|
import de.avatic.lcc.repositories.users.UserRepository;
|
||||||
import de.avatic.lcc.service.transformer.users.UserTransformer;
|
import de.avatic.lcc.service.transformer.users.UserTransformer;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -42,4 +45,21 @@ public class UserService {
|
||||||
userRepository.update(userTransformer.fromUserDTO(user));
|
userRepository.update(userTransformer.fromUserDTO(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSuper() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
|
//todo make a service. and simulate user rights in dev profile.
|
||||||
|
if (authentication != null && authentication.getPrincipal() instanceof LccOidcUser) {
|
||||||
|
LccOidcUser oidcUser = (LccOidcUser) authentication.getPrincipal();
|
||||||
|
|
||||||
|
return oidcUser.getAuthorities().stream().anyMatch(authority -> authority.getAuthority().equals("ROLE_SUPER"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public boolean canCalculate() {
|
||||||
|
// Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package de.avatic.lcc.service.users.authorization;
|
||||||
|
|
||||||
|
import de.avatic.lcc.dto.users.UserDTO;
|
||||||
|
import de.avatic.lcc.model.users.User;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public interface AuthorizationService {
|
||||||
|
default void setActiveUser(String id) { throw new UnsupportedOperationException(); }
|
||||||
|
|
||||||
|
User getActiveUser();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package de.avatic.lcc.service.users.authorization;
|
||||||
|
|
||||||
|
|
||||||
|
import de.avatic.lcc.model.users.User;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Profile("!dev & !test")
|
||||||
|
public class DefaultAuthorizationService implements AuthorizationService{
|
||||||
|
@Override
|
||||||
|
public User getActiveUser() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package de.avatic.lcc.service.users.authorization;
|
||||||
|
|
||||||
|
import de.avatic.lcc.dto.users.UserDTO;
|
||||||
|
import de.avatic.lcc.model.users.User;
|
||||||
|
import de.avatic.lcc.repositories.users.UserRepository;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Profile("dev | test")
|
||||||
|
public class SimulatedAuthorizationService implements AuthorizationService {
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private User activeUser;
|
||||||
|
|
||||||
|
public SimulatedAuthorizationService(UserRepository userRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setActiveUser(String mail) {
|
||||||
|
this.activeUser = userRepository.getByEmail(mail);
|
||||||
|
System.out.println(activeUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getActiveUser() {
|
||||||
|
if(activeUser == null){
|
||||||
|
activeUser = userRepository.getById(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -8,16 +8,16 @@ VALUES ('USR001', 'john.doe@company.com', 'John', 'Doe', TRUE),
|
||||||
ON DUPLICATE KEY UPDATE email = VALUES(email);
|
ON DUPLICATE KEY UPDATE email = VALUES(email);
|
||||||
|
|
||||||
INSERT INTO sys_group(group_name, group_description)
|
INSERT INTO sys_group(group_name, group_description)
|
||||||
VALUES ('default', 'Default user: Can login and generate reports');
|
VALUES ('basic', 'Login, generate reports');
|
||||||
INSERT INTO sys_group(group_name, group_description)
|
INSERT INTO sys_group(group_name, group_description)
|
||||||
VALUES ('LCE', 'Logistic cost expert: Can login, generate reports and do calculations');
|
VALUES ('calculation', 'Login, generate reports, do calculations');
|
||||||
INSERT INTO sys_group(group_name, group_description)
|
INSERT INTO sys_group(group_name, group_description)
|
||||||
VALUES ('freight', 'Freight key user: Can login, generate reports and edit freight rates');
|
VALUES ('freight', 'Login, generate reports, edit freight rates');
|
||||||
INSERT INTO sys_group(group_name, group_description)
|
INSERT INTO sys_group(group_name, group_description)
|
||||||
VALUES ('packaging', 'Packaging key user: Can login, generate reports and edit packaging data');
|
VALUES ('packaging', 'Login, generate reports, edit packaging data');
|
||||||
INSERT INTO sys_group(group_name, group_description)
|
INSERT INTO sys_group(group_name, group_description)
|
||||||
VALUES ('super',
|
VALUES ('super',
|
||||||
'Super key user: Can login, generate reports, do calculations, edit freight rates, edit packaging data');
|
'Login, generate reports, do calculations, edit freight rates, edit packaging data');
|
||||||
|
|
||||||
INSERT INTO sys_user_group_mapping (user_id, group_id)
|
INSERT INTO sys_user_group_mapping (user_id, group_id)
|
||||||
VALUES ((SELECT id FROM sys_group WHERE group_name = 'LCE'),
|
VALUES ((SELECT id FROM sys_group WHERE group_name = 'LCE'),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue