18 Commits

34 changed files with 3391 additions and 449 deletions

29
components.d.ts vendored Normal file
View File

@@ -0,0 +1,29 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
CancelationModal: typeof import('./src/components/CancelationModal.vue')['default']
Column: typeof import('primevue/column')['default']
ConfirmationModal: typeof import('./src/components/confirmationModal.vue')['default']
ConfirmedForm: typeof import('./src/components/ConfirmedForm.vue')['default']
DataTable: typeof import('primevue/datatable')['default']
IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
LoadingComponent: typeof import('./src/components/LoadingComponent.vue')['default']
LoginModal: typeof import('./src/components/LoginModal.vue')['default']
MainForm: typeof import('./src/components/MainForm.vue')['default']
NavBar: typeof import('./src/components/NavBar.vue')['default']
OrdersSelector: typeof import('./src/components/OrdersSelector.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SummaryComponent: typeof import('./src/components/SummaryComponent.vue')['default']
}
}

View File

@@ -4,9 +4,9 @@
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>Mleczarnia Kuzma</title>
</head>
<body>
<body class="has-navbar-fixed-top">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>

1452
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,13 +13,23 @@
"format": "prettier --write src/"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@primevue/themes": "^4.0.4",
"@vuepic/vue-datepicker": "^8.7.0",
"axios": "^1.7.2",
"bulma": "^1.0.1",
"cors": "^2.8.5",
"pinia": "^2.1.7",
"primevue": "^4.0.4",
"vee-validate": "^4.13.1",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
"vue-router": "^4.3.3",
"vue3-cookies": "^1.0.6",
"vue3-print-nb": "^0.1.4",
"yup": "^1.4.0"
},
"devDependencies": {
"@primevue/auto-import-resolver": "^4.0.4",
"@rushstack/eslint-patch": "^1.8.0",
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.12.5",
@@ -33,6 +43,7 @@
"prettier": "^3.2.5",
"sass": "^1.77.2",
"typescript": "~5.4.0",
"unplugin-vue-components": "^0.27.3",
"vite": "^5.2.8",
"vue-tsc": "^2.0.11"
}

View File

@@ -1,376 +1,27 @@
<template>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item">
<h3 class="title is-3">Mleczarnia</h3>
</a>
<button @click="makeBurger" class="button navbar-burger" data-target="navMenu" v-bind:class="{ 'is-active': activator }">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</button>
</div>
<div class="navbar-menu" id="navMenu" v-bind:class="{ 'is-active': activator }">
<div class="navbar-start">
<a class="navbar-item" @click="switchToFrom">
Formularz
</a>
<a class="navbar-item" @click="switchToOrders">
Zamówienia
</a>
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<a class="button is-light">
Log in
</a>
</div>
</div>
</div>
</div>
</nav>
<div v-if="isForm">
<form class="box" @submit="createJSON">
<div class="mb-3">
<div class="box">
<h1 class="is-large mb-3"><b>ZAMÓWIENIE</b></h1>
<h1 class="is-large mb-3" v-if="uuid != null" ><b>{{ uuid }}</b></h1>
<div class="field mb-5">
<label class="label is-small">NIP</label>
<div class="field">
<div class="select is-small is-expanded" style="width: 100%">
<select v-model="contractor" class="is-expanded" style="width: 100%" required>
<option class="is-expanded" v-for="contractor in contractors" :key="contractor" :value=contractor.Knt_KntId>{{ contractor.Knt_NipE + ', ' + contractor.Knt_Miasto + ', ' + contractor.Knt_Nazwa1 + ' ' + contractor.Knt_Nazwa2 + ' ' + contractor.Knt_Nazwa3 }}</option>
</select>
</div>
</div>
</div>
<div class="field">
<label class="label is-small">Data dostawy</label>
<div class="field is-small">
<VueDatePicker class ="bulma-is-small"
v-model="deliveryDate"
:enable-time-picker="false"
:clearable="true"
input-class-name="input is-small"
required/>
</div>
</div>
<button class="button is-primary mt-5">Submit</button>
<button class="button is-primary mt-5 ml-3" @click="showModal = true">Potwierdź</button>
</div>
</div>
<div v-for="(kategoria, index) in wares" :key="kategoria.Kod">
<div class="box" >
<h1 class="is-large mb-3"><b>{{ kategoria.Kod }}</b></h1>
<div class="field" v-for="(ware, index) in kategoria.Towary" :key="ware.Twr_Nazwa">
<label class="label is-small">{{ ware.Twr_Nazwa }}</label>
<div class="columns is-mobile">
<div class="column" v-if="ware.chosenOption == ware.Twr_JM">
<div class="field">
<input
class="input is-small"
type="text"
placeholder="Kwota"
v-model="ware.Twr_Cena"
/>
</div>
</div>
<div class="column" v-else-if="ware.chosenOption == ware.Twr_JMZ">
<div class="field">
<input
class="input is-small"
type="text"
placeholder="Kwota"
v-model="ware.Twr_CenaZ"
/>
</div>
</div>
<div class="column">
<div class="field has-addons">
<p class="control">
<span class="select is-small">
<select v-model="ware.chosenOption" readonly>
<option v-for="option in ware.Options" :key="option">{{ option }}</option>
</select>
</span>
</p>
<p class="control is-expanded">
<input class="input is-small" type="text" placeholder="Ilość" v-model="ware.Quantity">
</p>
</div>
</div>
</div>
<div v-if="!(index == kategoria.Towary.length - 1)"></div>
</div>
</div>
<div class="mb-3" v-if="!(index == wares.length - 1)"></div>
</div>
<button class="button is-primary mt-3 is-large is-fullwidth">Submit</button>
</form>
</div>
<div class="box is-shadowless" v-else-if="isOrders">
<form class="mb-3">
<div class="box">
<h1 class="is-large mb-3"><b>FILTR ZAMÓWIEŃ</b></h1>
<div class="field mb-5">
<label class="label is-small">Data zamówienia</label>
<div class="field is-small">
<VueDatePicker v-model="searchOrderDate"
:enable-time-picker="false"
:clearable="true"
input-class-name="input is-small"/>
</div>
</div>
<div class="field mb-5">
<label class="label is-small">Zamówienie potwierdzone?</label>
<div class="control">
<label class="checkbox mr-5">
<input type="checkbox" v-model="isOutOfBufor"/>
Tak
</label>
<label class="checkbox">
<input type="checkbox" v-model="isInBufor"/>
Nie
</label>
</div>
</div>
<button class="button is-primary is-small is-expanded" @click="createJSON">Pobierz zamówienia</button>
</div>
</form>
<div class="box">
<h1 class="is-large mb-3"><b>ZAMÓWIENIA</b></h1>
<div class="columns is-multiline">
<div class="column is-4" v-for="order in orders" :key="order.MZN_UUID">
<div class="box">
<h1 class="mb-3 is-size-7"><b>{{ order.MZN_UUID }}</b></h1>
<label class="label is-small">Klient</label>
<div class="field is-small mb-3">
<input class="input is-small is-static"
type="text"
:value="order.MZN_PodNazwa1 + order.MZN_PodNazwa2 + order.MZN_PodNazwa3"
readonly/>
</div>
<div class="columns is-mobile field is-small mb-0">
<div class="column is-6">
<label class="label is-small">Data dostawy</label>
<div class="field is-small">
<input class="input is-small is-static"
type="text"
v-model="order.MZN_DataDos"
readonly/>
</div>
</div>
<div class="column is-6">
<label class="label is-small">Data zamówienia</label>
<div class="field is-small">
<input class="input is-small is-static"
type="text"
v-model="order.MZN_DataZam.split('T')[0]"
readonly/>
</div>
</div>
</div>
<label class="label is-small">Zamówienie potwierdzone?</label>
<div class="field is-small mb-3" v-if="order.MZN_Bufor==0">
<input class="input is-small is-static"
type="text"
value="Tak"
readonly/>
</div>
<div class="field is-small mb-3" v-else>
<input class="input is-small is-static"
type="text"
value="Nie"
readonly/>
</div>
<button class="button is-primary is-small is-expanded" @click="viewOrder" :name="order.MZN_UUID">Podgląd</button>
</div>
</div>
</div>
</div>
</div>
<ConfirmationModal v-show="showModal" @close="showModal = false" :order-uuid="uuid"></ConfirmationModal>
<RouterView class="has-navbar-fixed-top"/>
</template>
<script lang="ts">
import './assets/style.scss';
import { ref } from 'vue';
import ConfirmationModal from '@/components/ConfirmationModal.vue';
<script setup lang="ts">
import './assets/style.scss'
import './assets/print.css'
import { RouterView } from 'vue-router'
import { useContractorsStore } from '@/stores/contractors.store'
import { useCategoriesStore } from '@/stores/categories.store'
export default {
components: { ConfirmationModal },
data() {
return {
contractors: [],
contractor: {},
wares: [],
orders: ref(new Array<Object>),
order: ref(),
deliveryDate: ref(),
searchOrderDate: ref(null),
isInBufor: ref(false),
isOutOfBufor: ref(false),
uuid: ref(),
activator: ref(false),
isForm: ref(true),
isOrders: ref(false),
showModal: ref(false)
}
},
watch: {
isInBufor(val) {
if(val == true && val == this.isOutOfBufor) {
this.isOutOfBufor = false;
}
},
isOutOfBufor(val) {
if(val == true && val == this.isInBufor) {
this.isInBufor = false;
}
}
},
methods: {
async fetchData() {
const response1 = await fetch('https://zamowienia.mleczarnia-kuzma.pl/api/kontrahenci');
this.contractors = await response1.json();
console.log(this.contractors);
const response2 = await fetch('https://zamowienia.mleczarnia-kuzma.pl/api/towary');
this.wares = await response2.json();
console.log(this.wares);
for (let kategoria of this.wares) {
for (let ware of kategoria.Towary) {
ware.Options = new Array(ware.Twr_JM);
ware.chosenOption = ware.Twr_JM;
if (ware.Twr_JMZ != null) {
ware.Options.push(ware.Twr_JMZ);
}
}
}
const response3 = await fetch('https://zamowienia.mleczarnia-kuzma.pl/api/zamowienia/bufor');
let ordersTemp : Array<Object> = await response3.json();
this.orders.push(...ordersTemp);
const response4 = await fetch('https://zamowienia.mleczarnia-kuzma.pl/api/zamowienia');
ordersTemp = await response4.json();
this.orders.push(...ordersTemp);
console.log(this.orders);
},
createJSON(event: Event) {
event.preventDefault();
console.log(this.contractor);
const json = {
MZN_UUID: this.uuid,
MZN_DataZam: new Date(Date.now()).toISOString(),
MZN_DataDos: this.deliveryDate != null ? this.deliveryDate.toISOString() : null,
MZN_PodID: this.contractor,
MZamElem: new Array()
};
for (let category of this.wares) {
for (let ware of category.Towary) {
if(ware.Quantity != null) {
ware.Options = new Array(ware.Twr_JM);
ware.chosenOption = ware.Twr_JM;
if (ware.Twr_JMZ != null) {
ware.Options.push(ware.Twr_JMZ);
}
const wareObject = {
MZE_TwrId: ware.Twr_TwrId,
MZE_TwrJM: ware.chosenOption,
MZE_TwrCena: ware.chosenOption == ware.Twr_JMZ ? ware.Twr_CenaZ : ware.Twr_Cena,
MZE_TwrIlosc: ware.Quantity
}
json.MZamElem.push(wareObject);
}
}
}
console.log(JSON.stringify(json));
fetch('https://zamowienia.mleczarnia-kuzma.pl/api/zamowienie', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(json)
})
.then(response => response.json())
.then(response => this.uuid = response.MZN_UUID)
},
parseOrderJSON(event: Event) {
event.preventDefault();
const json = "{\"MZN_UUID\":\"55ca3087-38c0-445f-ab7e-7e566c68c0d4\",\"MZN_DataZam\":1704063600000,\"MZN_DataDos\":1717493437880,\"MZN_PodID\":194,\"MZamElem\":[{\"MZE_TwrId\":1833,\"MZE_TwrJM\":\"kg\",\"MZE_TwrCena\":\"16.00\",\"MZN_TwrIlosc\":2},{\"MZE_TwrId\":1837,\"MZE_TwrJM\":\"kg\",\"MZE_TwrCena\":\"16.10\",\"MZN_TwrIlosc\":4}]}";
const parsedJson = JSON.parse(json);
console.log(this.deliveryDate.toJSON());
console.log(this.contractor);
fetch('https://zamowienia.mleczarnia-kuzma.pl/api/zamowienie/' + this.uuid, {
method: 'PUT'
})
},
async viewOrder(event: Event) {
console.log(event.target.name);
let tempOrder = this.orders.find(order => (order.MZN_UUID == event.target.name));
console.log(tempOrder);
const response = await fetch('https://zamowienia.mleczarnia-kuzma.pl/api/zamowienie/' + tempOrder.MZN_UUID);
let order = await response.json();
this.uuid = order.MZN_UUID;
this.contractor = order.MZN_PodID;
this.deliveryDate = new Date(order.MZN_DataDos);
console.log(order);
for(let product of order.MZamElem){
for(let kategoria of this.wares) {
let towar = kategoria.Towary.find(ware => (ware.Twr_TwrId == product.MZE_TwrId));
if(towar != null) {
console.log('ten towar ' + towar);
towar.Twr_Cena = product.MZE_TwrCena.slice(0, -2);
towar.Quantity = product.MZE_TwrIlosc.slice(0, -2);
break;
}
}
}
this.isForm = true;
this.isOrders = false;
window.scrollTo(0, 0);
},
makeBurger () {
this.activator = !this.activator
return this.activator
},
switchToFrom() {
if(!this.isForm) {
this.isForm = true;
this.isOrders = false;
if(this.activator) {
this.activator = false;
}
}
},
switchToOrders() {
if(!this.isOrders) {
this.isForm = false;
this.isOrders = true;
if(this.activator) {
this.activator = false;
}
}
}
},
created() {
this.fetchData()
}
}
// const categoriesStore = useCategoriesStore();
// await categoriesStore.fetchCategories();
</script>
<style>
@media (prefers-color-scheme: light) {
:root {
min-height: 100vh;
@media screen and (min-width: 500px) {
.box {
--bulma-box-padding: 1.5rem;
}
}
body {
min-height: 100vh;
@media screen and (max-width: 500px) {
.box {
--bulma-box-padding: 0.75rem;
}
}
</style>

View File

@@ -59,7 +59,6 @@
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:

View File

@@ -7,6 +7,15 @@
font-weight: normal;
}
.blackBorder {
--bulma-table-cell-border-color : black;
}
.tableOverflow {
overflow-x: scroll;
display: block;
}
a,
.green {
text-decoration: none;

8
src/assets/print.css Normal file
View File

@@ -0,0 +1,8 @@
.blackBorder {
--bulma-table-cell-border-color : black;
}
.tableOverflow {
overflow-x: scroll;
display: block;
}

View File

@@ -1,3 +1,22 @@
@import 'node_modules/bulma/bulma.scss';
@import '/home/patryk/WebstormProjects/Zamowienia-UI/node_modules/bulma/sass/utilities/mixins';
.element {
&.is-loading {
position: relative;
pointer-events: none;
opacity: 1;
&:after {
@include loader;
position: absolute;
top: calc(50% - 1em);
left: calc(50% - 1em);
width: 2em;
height: 2em;
border-width: 0.25em;
}
}
}
$primary-color: #111111;

View File

@@ -0,0 +1,45 @@
<script setup lang="ts">
import { useOrdersStore } from '@/stores/orders.store'
import { storeToRefs } from 'pinia'
import { axiosInstance } from '@/main'
import { useSiteControlStore } from '@/stores/siteControl.store'
import { useContractorsStore } from '@/stores/contractors.store'
import { useCategoriesStore } from '@/stores/categories.store'
const ordersStore = useOrdersStore();
const siteControlStore = useSiteControlStore();
const contractorStore = useContractorsStore();
const categoriesStore = useCategoriesStore();
const { uuid } = storeToRefs(ordersStore);
const { showCancellationModal, isLoading } = storeToRefs(siteControlStore);
async function cancelOrder() {
showCancellationModal.value = false;
axiosInstance.delete('/zamowienie/' + uuid.value);
siteControlStore.newOrder(true);
}
</script>
<template>
<div>
<div class="modal is-active">
<div class="modal-background"></div>
<div class="modal-card p-3">
<header class="modal-card-head">
<p class="modal-card-title">Uwaga</p>
<button class="delete" aria-label="close" @click="$emit('close')"></button>
</header>
<section class="modal-card-body">
Czy napewno chcesz anulować zamówienie? Czynności tej nie można odwrócić.
</section>
<footer class="modal-card-foot">
<div class="buttons">
<button class="button is-success" @click="cancelOrder">Tak</button>
<button class="button" @click="$emit('close')">Nie</button>
</div>
</footer>
</div>
</div>
</div>
</template>

View File

@@ -1,17 +1,33 @@
<script setup lang="ts">
const emit = defineEmits(['close']);
import { useOrdersStore } from '@/stores/orders.store'
import { storeToRefs } from 'pinia'
import { axiosInstance } from '@/main'
import { useSiteControlStore } from '@/stores/siteControl.store'
import { useContractorsStore } from '@/stores/contractors.store'
import { useCategoriesStore } from '@/stores/categories.store'
import { useRoute } from 'vue-router'
import { useRoutesStore } from '@/stores/routes.store'
const props = defineProps(
{
orderUuid : String
const ordersStore = useOrdersStore();
const siteControlStore = useSiteControlStore();
const contractorStore = useContractorsStore();
const categoriesStore = useCategoriesStore();
const routeStore = useRoutesStore();
const { contractor, contractors } = storeToRefs(contractorStore);
const { categories } = storeToRefs(categoriesStore);
const { uuid } = storeToRefs(ordersStore);
const { showConfirmationModal, isLoading} = storeToRefs(siteControlStore);
const { route, routes } = storeToRefs(routeStore);
async function confirmOrder() {
await axiosInstance.put('/zamowienie/' + uuid.value);
showConfirmationModal.value = false;
if (uuid.value != undefined && route.value != undefined) {
isLoading.value = true;
await ordersStore.loadOrder(uuid.value, true, contractor, contractors, categories, route, routes);
isLoading.value = false;
}
);
function confirmOrder() {
emit('close');
fetch('https://zamowienia.mleczarnia-kuzma.pl/api/zamowienie/' + props.orderUuid, {
method: 'PUT'
});
}
</script>
@@ -36,8 +52,4 @@ function confirmOrder() {
</div>
</div>
</div>
</template>
<style scoped>
</style>
</template>

View File

@@ -0,0 +1,147 @@
<script setup lang="ts">
import VueDatePicker from '@vuepic/vue-datepicker'
import { useOrdersStore } from '@/stores/orders.store'
import { storeToRefs } from 'pinia'
import { useCategoriesStore } from '@/stores/categories.store'
import { useContractorsStore } from '@/stores/contractors.store'
import { axiosInstance } from '@/main'
import { useRoutesStore } from '@/stores/routes.store'
const ordersStore = useOrdersStore();
const categoriesStore = useCategoriesStore();
const contractorsStore = useContractorsStore();
const routeStore = useRoutesStore();
const { order, uuid} = storeToRefs(ordersStore);
const { categories } = storeToRefs(categoriesStore);
const { contractor } = storeToRefs(contractorsStore);
const { route } = storeToRefs(routeStore);
function cancelOrder(event: Event) {
event.preventDefault();
axiosInstance.delete('/zamowienie/' + uuid.value);
}
</script>
<template>
<form class="box is-shadowless">
<div class="mb-3">
<div class="box">
<h1 class="title mb-3 is-6"><b>ZAMÓWIENIE</b></h1>
<h1 class="subtitle is-6 mb-3" v-if="uuid != null" ><b>{{ uuid }}</b></h1>
<div class="field mb-3" v-if="contractor != undefined">
<label class="label is-small">Klient</label>
<div class="field is-small mb-3">
<input class="input is-small is-static"
type="text"
:value="contractor.Knt_Nazwa1 + contractor.Knt_Nazwa2 + contractor.Knt_Nazwa3"
readonly/>
</div>
</div>
<div class="field mb-3" v-if="order != undefined">
<label class="label is-small">Data dostawy</label>
<div class="field is-small">
<VueDatePicker
v-model="order.MZN_DataDos"
:enable-time-picker="false"
:clearable="true"
input-class-name="input is-small calendar-background is-static"
menu-class-name="calendar-background"
readonly
hide-input-icon/>
</div>
</div>
<div class="field mb-3" v-if="order != undefined">
<label class="label is-small">Data zamówienia</label>
<div class="field is-small">
<VueDatePicker
v-model="order.MZN_DataZam"
:enable-time-picker="false"
:clearable="true"
input-class-name="input is-small calendar-background is-static"
menu-class-name="calendar-background"
readonly
hide-input-icon/>
</div>
</div>
<div class="field mb-3" v-if="route != undefined">
<label class="label is-small">Trasa</label>
<div class="field is-small mb-3">
<input class="input is-small is-static"
type="text"
:value="route.MZT_Nazwa1"
readonly/>
</div>
</div>
<button class="button is-danger" @click="cancelOrder">Anuluj zamówienie</button>
</div>
</div>
<div v-for="category in categories" :key="category.Kod">
<div class="box mb-3" v-if="category.isVisible">
<h1 class="title mb-3 is-6"><b>{{ category.Kod }}</b></h1>
<div class="field" v-for="(product) in category.Towary" :key="product.Twr_Nazwa">
<div v-if="Number(product.Quantity) > 0" class="mb-3">
<label class="label is-small">{{ product.Twr_Nazwa }}</label>
<div class="columns is-mobile">
<div class="column">
<div class="field">
<p class="control is-expanded">
<input class="input is-small is-static" type="text" placeholder="Ilość" :value="product.Quantity + ' ' + product.ChosenOption" readonly>
</p>
</div>
</div>
<div class="column" v-if="product.ChosenOption == product.Twr_JM">
<div class="field">
<input
class="input is-small is-static"
type="text"
placeholder="Kwota"
:value="product.Twr_Cena + ' PLN'"
readonly
/>
</div>
</div>
<div class="column" v-else-if="product.ChosenOption == product.Twr_JMZ">
<div class="field">
<input
class="input is-small is-static"
type="text"
placeholder="Kwota"
:value="product.Twr_CenaZ + ' PLN'"
readonly
/>
</div>
</div>
<div class="column" v-if="product.ChosenOption == product.Twr_JM">
<div class="field">
<input
class="input is-small is-static"
type="text"
placeholder="Kwota"
:value="(Number(product.Twr_Cena) * Number(product.Quantity)).toFixed(2) + ' PLN'"
readonly
/>
</div>
</div>
<div class="column" v-else-if="product.ChosenOption == product.Twr_JMZ">
<div class="field">
<input
class="input is-small is-static"
type="text"
placeholder="Kwota"
:value="(Number(product.Twr_CenaZ) * Number(product.Quantity)).toFixed(2) + ' PLN'"
readonly
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
</script>
<template>
<div class="box">
<div class="box is-skeleton m-3" style="height: 40vh"></div>
<div class="box is-skeleton mb-3 mx-3" style="height: 20vh"></div>
<div class="box is-skeleton mb-3 mx-3" style="height: 20vh"></div>
</div>
</template>

View File

@@ -0,0 +1,46 @@
<script setup lang="ts">
import { axiosInstance } from '@/main'
import type { VueCookies } from 'vue3-cookies/dist/interfaces'
import { inject } from 'vue'
const emit = defineEmits(['close']);
function sendLogin() {
emit('close');
axiosInstance.post('/login', {
username: 'testowyj',
password: 'beihiegei5Fied0b'
}, {
withCredentials: true
});
const $cookies = inject<VueCookies>('$cookies');
console.log($cookies);
}
</script>
<template>
<div>
<div class="modal is-active">
<div class="modal-background"></div>
<div class="modal-card p-3">
<header class="modal-card-head">
<p class="modal-card-title">Logowanie</p>
<button class="delete" aria-label="close" @click="$emit('close')"></button>
</header>
<section class="modal-card-body">
<form>
</form>
</section>
<footer class="modal-card-foot">
<div class="buttons">
<button class="button is-success" @click="sendLogin">Zaloguj się</button>
<button class="button" @click="$emit('close')">Anuluj</button>
</div>
</footer>
</div>
</div>
</div>
</template>

412
src/components/MainForm.vue Normal file
View File

@@ -0,0 +1,412 @@
<script setup lang="ts">
import VueDatePicker from '@vuepic/vue-datepicker';
import { axiosInstance, type Contractor, type OrderProduct, type Route } from '@/main'
import { useCategoriesStore } from '@/stores/categories.store'
import { useContractorsStore } from '@/stores/contractors.store'
import { useOrdersStore } from '@/stores/orders.store'
import { storeToRefs } from 'pinia'
import { useSiteControlStore } from '@/stores/siteControl.store'
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRoutesStore } from '@/stores/routes.store'
const categoriesStore = useCategoriesStore();
const contractorsStore = useContractorsStore();
const ordersStore = useOrdersStore();
const siteControlStore = useSiteControlStore();
const routesStore = useRoutesStore();
const { contractor, contractors } = storeToRefs(contractorsStore);
const { deliveryDate, uuid, additionalNotes } = storeToRefs(ordersStore);
const { categories } = storeToRefs(categoriesStore);
const { showConfirmationModal, showCancellationModal, isDarkTheme } = storeToRefs(siteControlStore);
const { route, routes } = storeToRefs(routesStore);
const contractorSearch = ref<string>();
const filteredContractors = ref<Array<Contractor>>();
const showContractorsDropdown = ref<boolean>(false);
const contractorInput = ref(null);
const routeSearch = ref<string>();
const filteredRoutes = ref<Array<Route>>();
const showRoutesDropdown = ref<boolean>(false);
const routeInput = ref(null);
const showErrorNotification = ref<boolean>(false);
const showSuccessNotification = ref<boolean>(false);
const errorNotificationMessage = ref<string>();
const successNotificationMessage = ref<string>();
watch(contractor, (contractor) => {
if(contractor == undefined) {
contractorSearch.value = '';
} else {
contractorSearch.value = contractor.Knt_NipE + ', ' + contractor.Knt_Nazwa1 + contractor.Knt_Nazwa2 + contractor.Knt_Nazwa3;
}
}, { immediate: true });
watch(route, (route) => {
if(route == undefined) {
routeSearch.value = '';
} else {
routeSearch.value = route.MZT_Nazwa1;
}
}, { immediate: true });
function createJSON(event: Event) {
event.preventDefault();
console.log(deliveryDate.value);
if(typeof deliveryDate.value != typeof Date) {
deliveryDate.value = new Date(deliveryDate.value as unknown as string);
}
const json = {
MZN_UUID: uuid.value,
MZN_DataZam: new Date(Date.now()).toISOString(),
MZN_DataDos: deliveryDate.value != undefined ? deliveryDate.value.toISOString().split('T')[0] : null,
MZN_PodID: contractor.value?.Knt_KntId,
MZN_MZTID: route.value?.MZT_MZTID,
MZamElem: new Array<OrderProduct>
};
if(categories.value == undefined) {
showErrorNotification.value=true;
errorNotificationMessage.value = "Produkty nie zostały pobrane z bazy danych.";
return;
}
if(contractor.value == undefined) {
showErrorNotification.value=true;
errorNotificationMessage.value = "Klient nie został wybrany.";
return;
}
if(deliveryDate.value == undefined) {
showErrorNotification.value=true;
errorNotificationMessage.value = "Data dostawy nie została wybrana.";
return;
}
for (let category of categories.value) {
for (let product of category.Towary) {
if(product.Quantity != null && product.Quantity != '') {
if(isNaN(Number(product.Quantity)) || isNaN(Number(product.Twr_CenaZ)) || isNaN(Number(product.Twr_Cena))
|| product.Twr_Cena == "" || product.Twr_CenaZ == "") {
showErrorNotification.value=true;
errorNotificationMessage.value = "W zamówieniu znajdują się niepoprawne wartości.";
return;
}
product.Twr_Cena = product.Twr_Cena == "" || product.Twr_Cena == null ? product.BasePrice : product.Twr_Cena;
product.Twr_CenaZ = product.Twr_CenaZ == "" || product.Twr_CenaZ == null ? product.BasePriceZ : product.Twr_CenaZ;
const productObject : OrderProduct = {
MZE_TwrId: product.Twr_TwrId,
MZE_TwrJm: product.ChosenOption,
MZE_TwrCena: product.ChosenOption == product.Twr_JMZ ? product.Twr_CenaZ : product.Twr_Cena,
MZE_TwrIlosc: product.Quantity,
MZE_TwrNazwa: product.Twr_Nazwa,
MZE_TwrKod: product.Twr_Kod,
MZE_MZNID: undefined,
MZE_MZEID: undefined,
MZE_TwrStawka: undefined
};
json.MZamElem.push(productObject);
}
}
}
if(json.MZamElem.length == 0) {
showErrorNotification.value=true;
errorNotificationMessage.value = "Zamówienie jest puste.";
return;
}
showErrorNotification.value=false;
console.log(json);
console.log(JSON.stringify(json));
axiosInstance.post('/zamowienie', JSON.stringify(json)).then( response => {
uuid.value = response.data.MZN_UUID;
showSuccessNotification.value = true;
successNotificationMessage.value = "Zamówienie zostało zapisane do bazy danych."
});
}
function setConfirmationModal(event : Event) {
event.preventDefault();
if(uuid.value == undefined) {
showErrorNotification.value=true;
errorNotificationMessage.value = "Zamówienie nie zostało jeszcze zapisane w bazie danych.";
return;
}
showConfirmationModal.value = true;
}
function cancelOrder(event: Event) {
event.preventDefault();
showCancellationModal.value = true;
}
function filterContractors() {
if (contractorSearch.value == "") {
contractor.value = undefined;
filteredContractors.value = contractors.value;
return;
}
filteredContractors.value = contractors.value.filter(
contractor =>
(contractor.Knt_NipE + contractor.Knt_Nazwa1 + contractor.Knt_Nazwa2 + contractor.Knt_Nazwa3).toLowerCase().includes(contractorSearch.value as string)
);
}
function toggleContractorsDropdown() {
if(!showContractorsDropdown.value) {
showContractorsDropdown.value = true;
if(contractorSearch.value == undefined || contractorSearch.value == '') {
filteredContractors.value = contractors.value;
}
}
}
function selectContractorFromDropdown(selectedContractor : Contractor) {
console.log(selectedContractor);
contractor.value = selectedContractor;
showContractorsDropdown.value = false;
}
function filterRoutes() {
if (routeSearch.value == "") {
route.value = undefined;
filteredRoutes.value = routes.value;
return;
}
if(routes.value != undefined) {
filteredRoutes.value = routes.value.filter(
route =>
route.MZT_Nazwa1.toLowerCase().includes(routeSearch.value?.toLowerCase() as string)
);
}
}
function toggleRoutesDropdown() {
if(!showRoutesDropdown.value) {
showRoutesDropdown.value = true;
if(routeSearch.value == undefined || routeSearch.value == '') {
filteredRoutes.value = routes.value;
}
}
}
function selectRouteFromDropdown(selectedRoute : Route) {
route.value = selectedRoute;
showRoutesDropdown.value = false;
}
function handleClickOutsideDropdown(event : Event) {
if(contractorInput.value != null && !contractorInput.value.contains(event.target)){
showContractorsDropdown.value = false;
}
if(routeInput.value != null && !routeInput.value.contains(event.target)){
showRoutesDropdown.value = false;
}
}
onMounted(function (){
document.addEventListener('click', handleClickOutsideDropdown);
});
onBeforeUnmount( function () {
document.removeEventListener('click', handleClickOutsideDropdown);
});
</script>
<template>
<form class="box is-shadowless" @submit.prevent="createJSON">
<div>
<div class="box mb-5">
<div class="mb-3">
<h1 class="title is-5"><b>ZAMÓWIENIE</b></h1>
<h1 class="subtitle is-5" v-if="uuid != undefined" ><b>{{ uuid }}</b></h1>
</div>
<div class="field mb-3">
<label class="label is-small">Klient</label>
<div class="field">
<div ref="contractorInput" class="dropdown maxwidth"
v-bind:class="{'is-active': showContractorsDropdown == true}">
<div class="dropdown-trigger maxwidth" @click="toggleContractorsDropdown">
<div class="field maxwidth">
<p class="control is-expanded has-icons-right is-small maxwidth">
<input class="input is-small is-expanded maxwidth" type="search"
v-model="contractorSearch" @input="filterContractors" />
<span class="icon is-small is-right"><i class="fas fa-search"></i></span>
</p>
</div>
</div>
<div class="dropdown-menu is-clipped has-background-info-on-scheme-invert" id="dropdown-menu" role="menu" style="max-width: calc(100vw - 3rem); box-shadow: 0px 0px 6px -1px rgba(165, 165, 165, 0.8); border-radius: 10px 10px 10px 10px; overflow-x:auto">
<div class="dropdown-content" style="max-height: 50vh; overflow-x: auto">
<a v-if="filteredContractors != undefined && filteredContractors.length == 0" class="dropdown-item is-clipped">Brak wyników</a>
<a v-for="dropdownContractor in filteredContractors" v-bind:key="dropdownContractor.Knt_KntId"
class="dropdown-item is-clipped" @click = "selectContractorFromDropdown(dropdownContractor)"
v-bind:class = "{'has-background-info' : dropdownContractor == contractor}">
{{dropdownContractor.Knt_NipE + ', ' + dropdownContractor.Knt_Nazwa1 + dropdownContractor.Knt_Nazwa2 + dropdownContractor.Knt_Nazwa3}}
</a>
</div>
</div>
</div>
</div>
</div>
<div class="field mb-3">
<label class="label is-small">Data dostawy</label>
<div class="field is-small">
<VueDatePicker class ="bulma-is-small"
v-model="deliveryDate"
:enable-time-picker="false"
:clearable="true"
input-class-name="input is-small calendar-background"
menu-class-name="calendar-background"
v-bind:dark = "!!window?.matchMedia?.('(prefers-color-scheme:dark)')?.matches"
/>
</div>
</div>
<div class="field mb-3">
<label class="label is-small">Trasa</label>
<div class="field">
<div ref="routeInput" class="dropdown maxwidth"
v-bind:class="{'is-active': showRoutesDropdown == true}">
<div class="dropdown-trigger maxwidth" @click="toggleRoutesDropdown">
<div class="field maxwidth">
<p class="control is-expanded has-icons-right is-small maxwidth">
<input class="input is-small is-expanded maxwidth" type="search"
v-model="routeSearch" @input="filterRoutes" />
<span class="icon is-small is-right"><i class="fas fa-search"></i></span>
</p>
</div>
</div>
<div class="dropdown-menu is-clipped has-background-info-on-scheme-invert" id="dropdown-menu" role="menu" style="max-width: calc(100vw - 3rem); box-shadow: 0px 0px 6px -1px rgba(165, 165, 165, 0.8); border-radius: 10px 10px 10px 10px; overflow-x:auto">
<div class="dropdown-content" style="max-height: 50vh; overflow-x: auto">
<a v-if="filteredRoutes != undefined && filteredRoutes.length == 0" class="dropdown-item is-clipped">Brak wyników</a>
<a class="dropdown-item is-clipped" @click = "selectRouteFromDropdown(dropdownRoute)"
v-bind:class = "{'has-background-info' : route == null}">Brak</a>
<a v-for="dropdownRoute in filteredRoutes" v-bind:key="dropdownRoute.MZT_MZTID"
class="dropdown-item is-clipped" @click = "selectRouteFromDropdown(dropdownRoute)"
v-bind:class = "{'has-background-info' : dropdownRoute == route}">
{{dropdownRoute.MZT_Nazwa1}}
</a>
</div>
</div>
</div>
</div>
</div>
<div class="field">
<label class="label is-small">Uwagi do zamówienia</label>
<textarea
v-model="additionalNotes"
class="textarea"
placeholder="Uwagi do zamówienia"
rows="5"
></textarea>
</div>
<button class="button is-info mt-3">Zapisz</button>
<button class="button is-success mt-3 ml-3" @click="setConfirmationModal" v-bind:disabled="uuid == undefined">Potwierdź</button>
<button class="button is-danger mt-3 ml-3" @click="cancelOrder" v-bind:disabled="uuid == undefined">Anuluj</button>
<div v-if="showErrorNotification" class="notification is-danger is-bold mt-3">
<button class="delete" @click.prevent="showErrorNotification = false"></button>
{{ errorNotificationMessage }}
</div>
<div v-if="showSuccessNotification" class="notification is-success is-bold mt-3">
<button class="delete" @click.prevent="showSuccessNotification = false"></button>
{{ successNotificationMessage }}
</div>
</div>
</div>
<div v-for="category in categories" :key="category.Kod">
<div class="box mb-3" >
<h1 class="title is-5 mb-3 mb-3"><b>{{ category.Kod }}</b></h1>
<div class="field" v-for="(product, index) in category.Towary" :key="product.Twr_Nazwa">
<label class="label is-small">{{ product.Twr_Nazwa }}</label>
<div class="columns is-mobile">
<div class="column" v-if="product.ChosenOption == product.Twr_JM">
<div class="field has-addons">
<p class="control is-expanded">
<input
class="input is-small is-fullwidth"
type="text"
placeholder="Kwota"
v-model="product.Twr_Cena"
v-bind:class="{ 'is-danger has-background-danger-soft': product.Twr_Cena != undefined && (isNaN(Number(product.Twr_Cena)) || product.Twr_Cena == ''),'is-success has-background-success-soft': product.Quantity != undefined && product.Quantity as unknown as string != '' && !isNaN(Number(product.Quantity))}"
/>
</p>
<p class="control">
<button class="button is-small is-danger is-outlined" @click.prevent="product.Twr_Cena = product.BasePrice">X</button>
</p>
</div>
</div>
<div class="column" v-else-if="product.ChosenOption == product.Twr_JMZ">
<div class="field has-addons">
<p class="control is-expanded">
<input
class="input is-small"
type="text"
placeholder="Kwota"
v-model="product.Twr_CenaZ"
v-bind:class="{ 'is-danger has-background-danger-soft': product.Twr_CenaZ != undefined && isNaN(Number(product.Twr_CenaZ)), 'is-success has-background-success-soft': product.Quantity != undefined && product.Quantity as unknown as string != '' && !isNaN(Number(product.Quantity))
}"
/>
</p>
<p class="control">
<button class="button is-small is-danger is-outlined" @click.prevent="product.Twr_CenaZ = product.BasePriceZ">X</button>
</p>
</div>
</div>
<div class="column">
<div class="field has-addons">
<p class="control">
<span class="select is-small" v-bind:class="{ 'is-danger': product.Quantity != undefined && isNaN(Number(product.Quantity)),'is-success': product.Quantity != undefined && product.Quantity as unknown as string != '' && !isNaN(Number(product.Quantity))}">
<select v-model="product.ChosenOption" v-bind:class="{ 'is-danger has-background-danger-soft': product.Quantity != undefined && isNaN(Number(product.Quantity)),'is-success has-background-success-soft': product.Quantity != undefined && product.Quantity as unknown as string != '' && !isNaN(Number(product.Quantity))}">
<option v-for="option in product.Options" :key="option">{{ option }}</option>
</select>
</span>
</p>
<p class="control is-expanded">
<input class="input is-small" type="text" placeholder="Ilość" v-model="product.Quantity" v-bind:class="{ 'is-danger has-background-danger-soft': product.Quantity != undefined && isNaN(Number(product.Quantity)),'is-success has-background-success-soft': product.Quantity != undefined && product.Quantity as unknown as string != '' && !isNaN(Number(product.Quantity))}">
</p>
</div>
</div>
</div>
<div v-if="!(index == category.Towary.length - 1)"></div>
</div>
</div>
</div>
<div class="columns mt-1 is-variable is-mobile">
<div class = "column is-6">
<button class="button is-info is-fullwidth is-large">Zapisz</button>
</div>
<div class = "is-large column is-6 ml-3">
<button class="button is-success is-fullwidth is-large" @click="showConfirmationModal = true">Potwierdź</button>
</div>
</div>
</form>
</template>
<style>
@media (prefers-color-scheme: dark){
.calendar-background {
--dp-background-color: rgb(20, 22, 26);
--dp-border-color: var(--bulma-border);
--dp-menu-border-color: var(--bulma-border);
--dp-border-color-hover: var(--bulma-border);
--dp-border-color-focus: var(--bulma-border);
--dp-primary-color: var(--bulma-info);
--dp-primary-text-color: #000;
}
}
@media (prefers-color-scheme: light){
.calendar-background {
--dp-border-color: var(--bulma-border);
--dp-menu-border-color: var(--bulma-border);
--dp-border-color-hover: var(--bulma-border);
--dp-border-color-focus: var(--bulma-border);
--dp-primary-color: var(--bulma-info);
--dp-primary-text-color: #000;
}
}
.maxwidth {
width: 100%;
}
</style>

94
src/components/NavBar.vue Normal file
View File

@@ -0,0 +1,94 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useSiteControlStore } from '@/stores/siteControl.store'
import { router } from '@/router/router'
import { useOrdersStore } from '@/stores/orders.store'
import { useContractorsStore } from '@/stores/contractors.store'
import { useCategoriesStore } from '@/stores/categories.store'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user.store'
import { axiosInstance } from '@/main'
const activator = ref(false);
const siteControlStore = useSiteControlStore();
const ordersStore = useOrdersStore();
const contractorsStore = useContractorsStore();
const categoriesStore = useCategoriesStore();
const userStore = useUserStore();
const { username } = storeToRefs(userStore);
function makeBurger() {
activator.value = !activator.value
return activator
}
function clickForm() {
siteControlStore.switchToForm();
if(activator.value) {
activator.value = false;
}
}
function clickOrders() {
siteControlStore.switchToOrders();
if(activator.value) {
activator.value = false;
}
}
function clickTable() {
siteControlStore.switchToTable();
if(activator.value) {
activator.value = false;
}
}
function routeLogin() {
axiosInstance.post('/logout');
router.push("/login");
}
</script>
<template>
<nav class="navbar has-shadow is-fixed-top" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item is-overflow-hidden" style="max-width: calc(100vw - 50px); white-space: nowrap; overflow: hidden">
<h3 class="title is-4">Mleczarnia</h3>
<h4 v-if="username != undefined" class="subtitle is-4">&nbsp;{{'- ' + username}}</h4>
</a>
<button @click="makeBurger" class="button navbar-burger is-pulled-right" data-target="navMenu" v-bind:class="{ 'is-active': activator }">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</button>
</div>
<div class="navbar-menu" id="navMenu" v-bind:class="{ 'is-active': activator }">
<div class="navbar-start">
<a class="navbar-item" @click="clickForm">
Formularz
</a>
<a class="navbar-item" @click="clickOrders">
Zamówienia
</a>
<a class="navbar-item" @click="clickTable">
Zestawienie
</a>
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<button class="button is-info" @click="siteControlStore.newOrder">
Nowe Zamówienie
</button>
<button class="button is-info" @click="routeLogin" >
Wyloguj
</button>
</div>
</div>
</div>
</div>
</nav>
</template>

View File

@@ -0,0 +1,209 @@
<script setup lang="ts">
import VueDatePicker from '@vuepic/vue-datepicker';
import { computed, ref, watch } from 'vue'
import { useOrdersStore } from '@/stores/orders.store'
import { storeToRefs } from 'pinia'
import { useSiteControlStore } from '@/stores/siteControl.store'
import type { Order } from '@/main'
const ordersStore = useOrdersStore();
const siteControlStore = useSiteControlStore();
const searchOrderDate = ref<Array<Date>>([]);
const isOutOfBuffer = ref<boolean>(false);
const isInBufor = ref<boolean>(false);
const { orders, dates } = storeToRefs(ordersStore);
const areOrdersLoading = ref<boolean>(false);
const { isDarkTheme, isLoading } = storeToRefs(siteControlStore);
const date = new Date(Date.now());
const startDate = new Date(date.getFullYear(), date.getMonth(), (date.getDate() - 2));
const endDate = new Date(date.getFullYear()+1, date.getMonth(), date.getDay());
searchOrderDate.value?.push(startDate, endDate);
watch(isInBufor, (val) => {
if(val && val == isOutOfBuffer.value) {
isOutOfBuffer.value = false;
}
},{
immediate: true
});
watch(isOutOfBuffer, (val) => {
if(val && val == isInBufor.value) {
isInBufor.value = false;
}
}, {
immediate: true
});
const buffer = computed(()=>{
if (!isInBufor.value && !isOutOfBuffer.value) {
return null;
} else return !!isInBufor.value;
});
const datesWithOrders = computed( ()=>{
const datesWithOrders = new Map<Date, Array<Order>>();
if (dates.value == undefined || orders.value == undefined) {
return datesWithOrders;
}
for (const date of dates.value) {
datesWithOrders.set(date, []);
for (const order of orders.value) {
if (order.MZN_DataDos == date.toString()) {
datesWithOrders.get(date)?.push(order);
}
}
}
return datesWithOrders;
})
function viewOrder(order : Order) {
order.loading = true;
siteControlStore.viewOrder(order.MZN_UUID);
}
async function fetchOrders(event : Event) {
event?.preventDefault();
areOrdersLoading.value = true;
console.log(searchOrderDate.value);
if(searchOrderDate.value == undefined) {
orders.value = await ordersStore.fetchOrdersByBuffer(buffer.value);
}
if(searchOrderDate.value != undefined) {
orders.value = await ordersStore.fetchOrdersByDateStartAndEnd(searchOrderDate.value[0], searchOrderDate.value[1], buffer.value);
}
areOrdersLoading.value = false;
}
fetchOrders(null);
</script>
<template>
<div class="box is-shadowless">
<form class="mb-3">
<div class="box">
<h1 class="mb-3 title is-5"><b>FILTR ZAMÓWIEŃ</b></h1>
<div class="field mb-5">
<label class="label is-small">Data zamówienia</label>
<div class="field is-small">
<VueDatePicker v-model="searchOrderDate"
:enable-time-picker="false"
:clearable="true"
:highlight="dates"
input-class-name="input is-small calendar-background"
menu-class-name="calendar-background"
range/>
</div>
</div>
<div class="field mb-5">
<label class="label is-small">Zamówienie potwierdzone?</label>
<div class="control">
<label class="checkbox mr-5">
<input type="checkbox" v-model="isOutOfBuffer"/>
Tak
</label>
<label class="checkbox">
<input type="checkbox" v-model="isInBufor"/>
Nie
</label>
</div>
</div>
<button class="button is-info is-small is-expanded" @click="fetchOrders" :class="{ 'is-loading': areOrdersLoading }">Pobierz zamówienia</button>
</div>
</form>
<div class="box">
<h1 class="mb-3 title is-5"><b>ZAMÓWIENIA</b></h1>
<div v-for="date in datesWithOrders.keys()" :key="String(date)">
<div v-if="datesWithOrders.get(date)?.length != 0">
<h1 class="mb-3 subtitle is-6" :class="{'is-skeleton' : areOrdersLoading}">{{ date }}</h1>
<div class="columns is-multiline">
<template v-for="order in datesWithOrders.get(date)" :key="order.MZN_UUID">
<div class="column is-4 mb-3" v-if="order.MZN_DataDos == date.toString()">
<div class="box"
:class="{'confirmed' : order.MZN_Bufor == 0 && order.MZN_Anulowane != 1,
'cancelled' : order.MZN_Anulowane == 1,
'is-skeleton' : areOrdersLoading}">
<label class="label is-small" :class="{'is-invisible': areOrdersLoading}">Klient</label>
<div class="field is-small mb-3" :class="{'is-invisible': areOrdersLoading}">
<input class="input is-small is-static"
type="text"
:value="order.MZN_PodNazwa1 + order.MZN_PodNazwa2 + order.MZN_PodNazwa3"
readonly/>
</div>
<div class="columns is-mobile field is-small mb-0">
<div class="column is-6" :class="{'is-invisible': areOrdersLoading}">
<label class="label is-small">Data dostawy</label>
<div class="field is-small">
<input class="input is-small is-static"
type="text"
v-model="order.MZN_DataDos"
readonly/>
</div>
</div>
<div class="column is-6">
<label class="label is-small" :class="{'is-invisible': areOrdersLoading}">Zamówienie potwierdzone?</label>
<div class="field is-small" :class="{'is-invisible': areOrdersLoading}" v-if="order.MZN_Bufor==0">
<input class="input is-small is-static"
type="text"
value="Tak"
readonly/>
</div>
<div class="field is-small" :class="{'is-invisible': areOrdersLoading}" v-else>
<input class="input is-small is-static"
type="text"
value="Nie"
readonly/>
</div>
</div>
</div>
<label class="label is-small mb-5" :class="{'is-invisible': areOrdersLoading}">Produkty</label>
<div class="field columns is-multiline is-mobile">
<template v-for="product in order.MZamElem" :key="product.MZE_TwrKod">
<div class="column is-6 py-0">{{ product.MZE_TwrKod }}</div>
<div class="column is-6 py-0">{{Number(product.MZE_TwrIlosc).toFixed(2) + " " + product.MZE_TwrJm}}</div>
</template>
</div>
<button class="button is-info is-small is-expanded" :class="{'is-invisible': areOrdersLoading, 'is-loading': order.loading}" @click="viewOrder(order)">Podgląd</button>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
</template>
<style>
@media (prefers-color-scheme: dark){
.calendar-background {
--dp-background-color: rgb(20, 22, 26);
--dp-border-color: var(--bulma-border);
--dp-menu-border-color: var(--bulma-border);
--dp-border-color-hover: var(--bulma-border);
--dp-border-color-focus: var(--bulma-border);
--dp-primary-color: var(--bulma-info);
--dp-primary-text-color: #000;
}
}
@media (prefers-color-scheme: light){
.calendar-background {
--dp-border-color: var(--bulma-border);
--dp-menu-border-color: var(--bulma-border);
--dp-border-color-hover: var(--bulma-border);
--dp-border-color-focus: var(--bulma-border);
--dp-primary-color: var(--bulma-info);
--dp-primary-text-color: #000;
}
}
.cancelled {
--bulma-box-background-color: var(--bulma-danger-soft)
}
.confirmed {
--bulma-box-background-color: var(--bulma-success-soft)
}
</style>

View File

@@ -0,0 +1,211 @@
<script setup lang="ts">
import { useOrdersStore } from '@/stores/orders.store'
import { storeToRefs } from 'pinia'
import VueDatePicker from '@vuepic/vue-datepicker'
import { useSiteControlStore } from '@/stores/siteControl.store'
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { useCategoriesStore } from '@/stores/categories.store'
import type { Product } from '@/main'
const ordersStore = useOrdersStore();
const categoriesStore = useCategoriesStore();
const { orders, dates } = storeToRefs(ordersStore);
const searchDate = ref<Date>(new Date(Date.now()));
const confirmedOrders = ref<boolean>();
const isSummed = ref<boolean>(true);
const isLoading = ref<boolean>(true);
const summedProducts = ref<Array<Product>>([]);
const products = ref<Array<Object>>();
async function fetchOrders() {
isLoading.value=true;
console.log((confirmedOrders.value) ? true : null);
orders.value = await ordersStore.fetchOrdersByDateStartAndEnd(searchDate.value != undefined ? searchDate.value : new Date(Date.now()),
searchDate.value != undefined ? searchDate.value : new Date(Date.now()),
(confirmedOrders.value) ? true : null);
const productsMap = await categoriesStore.sumProductsFromOrders(orders.value);
for(const product of productsMap.values()) {
if(product.SummedQuantity > 0) {
summedProducts.value.push(product);
}
}
await prepareProductsFromOrders();
isLoading.value=false;
}
async function prepareProductsFromOrders() {
products.value = [];
if(orders.value == undefined) {
return;
}
for(const order of orders.value) {
for (const product of order.MZamElem) {
const newProduct = {
'kod': product.MZE_TwrKod,
'nazwa': product.MZE_TwrNazwa,
'ilosc': Number(product.MZE_TwrIlosc).toFixed(2),
'jm': product.MZE_TwrJm,
'cena': Number(product.MZE_TwrCena).toFixed(2),
'suma': Number(Number(product.MZE_TwrCena) * Number(product.MZE_TwrIlosc)).toFixed(2),
'order': {
'uuid': order.MZN_UUID,
'nazwaklienta': order.MZN_PodNazwa1 + order.MZN_PodNazwa2 + order.MZN_PodNazwa3
}
}
products.value.push(newProduct);
}
}
}
onMounted(async () => {
orders.value = await ordersStore.fetchOrdersByDay(searchDate.value, null);
const productsMap = await categoriesStore.sumProductsFromOrders(orders.value);
summedProducts.value = [];
for(const product of productsMap.values()) {
if(product.SummedQuantity > 0) {
summedProducts.value.push(product);
}
}
console.log(summedProducts.value);
await prepareProductsFromOrders();
console.log(products.value);
isLoading.value=false;
});
onBeforeUnmount( async function (){
const siteControlStore = useSiteControlStore();
await siteControlStore.newOrder(false);
})
</script>
<template>
<div class="columns box is-shadowless" style="margin: 0.5rem 0 0; padding: 0; min-height: 100%">
<div class="column">
<div class="box">
<h1 class="title is-4 mb-3">Opcje</h1>
<label class="label is-small">Data dostawy</label>
<VueDatePicker v-model="searchDate"
:enable-time-picker="false"
:clearable="true"
input-class-name="input is-small calendar-background"
menu-class-name="calendar-background"
:highlight = "dates"
/>
<div class="control mt-3">
<label class="checkbox mr-5">
<input type="checkbox" v-model="confirmedOrders"/>
Tylko potwierdzone zamówienia?
</label>
</div>
<button class="button mt-3" @click="fetchOrders">Potwierdź</button>
</div>
<div class="box mt-3">
<button class="button is-fullwidth mb-3" @click="isSummed=false">Rozdzielone zamówienia</button>
<button class="button is-fullwidth mb-3" @click="isSummed=true">Zsumowane zamówienia</button>
<button class="button is-fullwidth" v-print="'#printMe'">Drukuj</button>
</div>
</div>
<div class="column is-four-fifths">
<div class="box" style="width: 100%; height: 100%; padding: 0">
<div style="width: 100%; height: 100%">
<div v-if="isLoading == true" class="is-flex is-justify-content-center is-flex-direction-row" style="height: 100%; align-content:space-evenly">
<div class="title is-1 has-text-centered element is-loading" style="min-height: 150px; align-self: center;"></div>
</div>
<DataTable :value="products" class="mb-3" style="padding:0" scrollable id="printMe" rowGroupMode="subheader" groupRowsBy="order.uuid" v-else-if="orders != undefined && orders.length != 0 && !isSummed">
<Column field="order.uuid" header="UUID"/>
<Column field="kod" header="Indeks" frozen></Column>
<Column field="nazwa" header="Nazwa"/>
<Column field="ilosc" header="Ilość"/>
<Column field="jm" header="JM"/>
<Column field="cena" header="Cena"/>
<Column field="suma" header="Suma"/>
<template #groupheader="slotProps">
<span>{{ slotProps.data.order.nazwaklienta }}</span>
</template>
</DataTable>
<DataTable :value="summedProducts" class="mb-3" style="padding:0" scrollable id="printMe" v-else-if="orders != undefined && orders.length != 0 && isSummed">
<Column field="Twr_Kod" header="Indeks" frozen></Column>
<Column field="Twr_Nazwa" header="Nazwa"/>
<Column field="SummedQuantity" header="Ilość">
<template #body="slotProps">
<span>{{ slotProps.data.SummedQuantity.toFixed(2)}}</span>
</template>
</Column>
<Column field="Twr_JM" header="JM">
</Column>
<Column field="SummedPrice" header="Suma">
<template #body="slotProps">
<span>{{ slotProps.data.SummedPrice.toFixed(2)}}</span>
</template>
</Column>
</DataTable>
<div v-else class="is-flex is-justify-content-center is-flex-direction-row">
<p class="title is-1 has-text-centered" style="height: min-content; align-self: center;">Brak zamówień</p>
</div>
</div>
</div>
</div>
</div>
</template>
<style>
@media screen and (min-width: 500px) {
.box {
--bulma-box-padding: 1.5rem;
}
}
@media screen and (max-width: 500px) {
.box {
--bulma-box-padding: 0.75rem;
}
}
.blackBorder {
--bulma-table-cell-border-color : black;
}
.tableOverflow {
overflow-x: scroll;
display: block;
padding: 0;
}
.test {
padding: 0.75rem;
}
tr th:first-child{
border-top-left-radius: 0.75rem;
}
tr th:last-child{
border-top-right-radius: 0.75rem;
}
.p-datatable-scrollable {
border-top-right-radius: 0.75rem;
border-top-left-radius: 0.75rem;
}
.p-datatable-table-container {
border-top-right-radius: 0.75rem;
border-top-left-radius: 0.75rem;
}
table {
border-top-right-radius: 0.75rem;
border-top-left-radius: 0.75rem;
}
@media print {
.dashedBorder{
border-style: dotted;
border-color: lightgray;
}
}
</style>

View File

@@ -1,8 +1,172 @@
import { createApp } from 'vue'
import App from './App.vue'
import { createApp, watch } from 'vue'
import App from './App.vue';
import VueDatePicker from '@vuepic/vue-datepicker';
import '@vuepic/vue-datepicker/dist/main.css'
import '@vuepic/vue-datepicker/dist/main.css';
import ConfirmationModal from '@/components/ConfirmationModal.vue';
import NavBar from '@/components/NavBar.vue';
import MainForm from '@/components/MainForm.vue';
import OrdersSelector from '@/components/OrdersSelector.vue'
import ConfirmedForm from '@/components/ConfirmedForm.vue'
import LoadingComponent from '@/components/LoadingComponent.vue'
import axios from 'axios'
import LoginModal from '@/components/LoginModal.vue'
import { createPinia, storeToRefs } from 'pinia'
import VueCookies from 'vue3-cookies'
import { router } from '@/router/router'
import { useUserStore } from '@/stores/user.store'
import print from 'vue3-print-nb'
import CancelationModal from '@/components/CancelationModal.vue'
import SummaryComponent from '@/components/SummaryComponent.vue'
import PrimeVue from "primevue/config";
import Lara from '@primevue/themes/lara';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.use(VueCookies);
app.use(router);
app.use(print);
app.use(PrimeVue, {
// Default theme configuration
theme: {
preset: Lara,
options: {
prefix: 'p',
darkModeSelector: 'system',
cssLayer: false
}
}
});
if(localStorage.getItem('piniaState')) {
pinia.state.value = JSON.parse(localStorage.getItem('piniaState') as string);
}
watch (
() => pinia.state.value,
(state) => {
localStorage.setItem('piniaState', JSON.stringify(state));
},
{deep: true}
)
export const axiosInstance = axios.create({
baseURL: 'https://zamowienia.mleczarnia-kuzma.pl/api',
withCredentials: true
});
axiosInstance.interceptors.response.use( (response) => {
return response;
}, (error) => {
if (error.response.status == 401) {
const userStore = useUserStore();
storeToRefs(userStore).username.value = undefined;
router.push('/login');
}
});
const app = createApp(App)
app.component('VueDatePicker', VueDatePicker);
app.mount('#app')
app.component('ConfirmationModal', ConfirmationModal);
app.component('LoginModal', LoginModal);
app.component('NavBar', NavBar);
app.component('MainForm', MainForm);
app.component('OrdersSelector', OrdersSelector);
app.component('ConfirmedForm', ConfirmedForm);
app.component('LoadingComponent', LoadingComponent);
app.component('CancelationModal', CancelationModal);
app.component('SummaryComponent', SummaryComponent);
app.mount('#app');
export interface Category {
GIDNumer: number,
GrONumer: number,
Kod: string,
Nazwa: string,
Poziom: number,
Sciezka: string,
Towary: Array<Product>
isVisible: boolean
}
export interface Product {
Twr_Cena: string,
Twr_CenaZ: string,
Twr_JM: string,
Twr_JMPrzelicznikL: string,
Twr_JMPrzelicznikM: string,
Twr_JMZ: string,
Twr_Kod: string,
Twr_Nazwa: string,
Twr_NieAktywny: number,
Twr_Stawka: string,
Twr_TwGGIDNumer: number,
Twr_TwrId: number,
Options: Array<string>,
ChosenOption: string,
Quantity: string,
BasePrice: string,
BasePriceZ: string,
SummedQuantity: number,
SummedPrice: number
}
export interface Contractor {
Knt_Email: string,
Knt_KntId: number,
Knt_KodPocztowy: string,
Knt_Miasto: string,
Knt_Nazwa1: string,
Knt_Nazwa2: string,
Knt_Nazwa3: string,
Knt_Nieaktywny: number,
Knt_NipE: string,
Knt_NrDomu: string,
Knt_OpiekunId: number,
Knt_OpiekunTyp: number,
Knt_Ulica: string,
Knt_Wojewodztwo: string
}
export interface Order {
loading: boolean
MZN_Bufor: number,
MZN_Anulowane: number,
MZN_DataDos: string,
MZN_DataZam: string,
MZN_MZNID: number,
MZN_OpeID: number,
MZN_PodID: number,
MZN_PodKodPocztowy: string,
MZN_PodMiasto: string,
MZN_PodNazwa1: string,
MZN_PodNazwa2: string,
MZN_PodNazwa3: string,
MZN_PodNipE: string,
MZN_PodNrDomu: string,
MZN_PodUlica: string,
MZN_PodWojewodztwo: string,
MZN_TypDokumentu: number,
MZN_UUID: string,
MZN_Uwagi: string,
MZN_MZTID: number,
MZamElem: Array<OrderProduct>
}
export interface OrderProduct {
MZE_MZEID: number | undefined,
MZE_MZNID: number | undefined,
MZE_TwrCena: string,
MZE_TwrId: number,
MZE_TwrIlosc: string,
MZE_TwrJm: string,
MZE_TwrNazwa: string,
MZE_TwrKod: string,
MZE_TwrStawka: string | undefined,
}
export interface Route {
MZT_MZTID: number,
MZT_Nazwa1: string
}

15
src/router/router.ts Normal file
View File

@@ -0,0 +1,15 @@
import { createRouter, createWebHistory } from 'vue-router';
import { MainView, LoginView, SummaryView, OrdersView } from '@/views'
export const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
linkActiveClass: 'active',
routes: [
{ path: '/', component: MainView },
{ path: '/login', component: LoginView },
{ path: '/summary', component: SummaryView, },
{ path: '/orders', component: OrdersView}
]
});

View File

@@ -0,0 +1,62 @@
import { defineStore } from 'pinia'
import type { Category, Order, Product } from '@/main'
import { ref } from 'vue'
import { axiosInstance } from '@/main'
export const useCategoriesStore = defineStore('categories', () => {
const categories = ref<Array<Category>>([]);
async function fetchCategories() {
const response = await axiosInstance.get('/towary', {withCredentials: true});
categories.value = response.data;
if(categories.value != undefined) {
for (const category of categories.value) {
for (const product of category.Towary) {
product.Options = new Array(product.Twr_JM);
product.ChosenOption = product.Twr_JM;
product.BasePrice = product.Twr_Cena;
product.BasePriceZ = product.Twr_CenaZ;
product.SummedQuantity = 0;
product.SummedPrice = 0;
if (product.Twr_JMZ != null) {
product.Options.push(product.Twr_JMZ);
}
}
}
}
}
async function sumProductsFromOrders(orders : Array<Order>) {
const productsMap = new Map<string, Product>();
await fetchCategories();
if(categories.value != undefined) {
for (const category of categories.value) {
for (const product of category.Towary) {
productsMap.set(product.Twr_TwrId.toString(), product);
}
}
}
if(orders != undefined) {
for (const order of orders) {
for (const product of order.MZamElem) {
const mapProduct = productsMap.get(String(product.MZE_TwrId));
if (product.MZE_TwrJm == mapProduct?.Twr_JM) {
mapProduct.SummedQuantity += Number(product.MZE_TwrIlosc);
mapProduct.SummedPrice += (Number(product.MZE_TwrCena) * Number(product.MZE_TwrIlosc));
}
else if (product.MZE_TwrJm == mapProduct?.Twr_JMZ) {
mapProduct.SummedQuantity += (Number(product.MZE_TwrIlosc) * Number(mapProduct.Twr_JMPrzelicznikM))/Number(mapProduct.Twr_JMPrzelicznikL);
mapProduct.SummedPrice += (Number(product.MZE_TwrCena) * Number(product.MZE_TwrIlosc));
}
}
}
}
return productsMap;
}
return {categories, fetchCategories, sumProductsFromOrders}
})

View File

@@ -0,0 +1,21 @@
import { defineStore } from 'pinia'
import type { Contractor } from '@/main'
import { ref } from 'vue'
import { axiosInstance } from '@/main'
export const useContractorsStore = defineStore('contractors', () => {
const contractors = ref<Array<Contractor>>([]);
contractors.value=[];
const contractor = ref<Contractor>();
async function fetchContractors() {
const response = await axiosInstance.get('/kontrahenci', {withCredentials: true});
console.log(response);
console.log(contractors.value);
contractors.value = [];
contractors.value.push(...response.data);
}
return {contractors, contractor, fetchContractors}
})

114
src/stores/orders.store.ts Normal file
View File

@@ -0,0 +1,114 @@
import { defineStore } from 'pinia'
import type { Category, Contractor, Order, Route } from '@/main'
import { type Ref, ref } from 'vue'
import { axiosInstance } from '@/main'
export const useOrdersStore = defineStore('orders', () => {
const orders = ref<Array<Order>>();
const dates = ref<Date[]>();
const order = ref<Order>();
const uuid = ref<string>();
const deliveryDate = ref<Date>();
const orderDate = ref<Date>();
const additionalNotes = ref<string>();
async function fetchOrders() {
const response = await axiosInstance.get('/zamowienia', {withCredentials: true});
const ordersTemp : Array<Order> = response.data;
await getOrderDates();
return ordersTemp;
}
async function fetchOrdersByBuffer(inBuffer : boolean | null) {
let urlString = '/zamowienia';
if(inBuffer != null) {
urlString += '?bufor=' + Number(inBuffer).toString();
}
const response = await axiosInstance.get(urlString, {withCredentials: true});
const ordersTemp : Array<Order> = response.data;
await getOrderDates();
return ordersTemp;
}
async function fetchOrdersByDateStartAndEnd(dateStart : Date, dateEnd : Date, inBuffer : boolean | null) {
let urlString = '/zamowienia?od=' + dateStart.toISOString().split('T')[0] + '&do=' + dateEnd.toISOString().split('T')[0]
if(inBuffer != null) {
urlString += '&bufor=' + Number(inBuffer).toString();
}
const response = await axiosInstance.get(urlString, {withCredentials: true});
const ordersTemp : Array<Order> = response.data;
await getOrderDates();
return ordersTemp;
}
async function fetchOrdersByDay(date : Date, inBuffer : boolean | null) {
let urlString = "/zamowienia/" + date.toISOString().split("T")[0];
console.log(urlString);
if(inBuffer != null) {
urlString += '?bufor=' + Number(inBuffer).toString();
}
const response = await axiosInstance.get(urlString, {withCredentials: true});
const ordersTemp : Array<Order> = response.data;
await getOrderDates();
return ordersTemp;
}
async function getOrderDates() {
const tempDates = new Array<Date>();
tempDates.push(...await fetchDates(0));
tempDates.push(...await fetchDates(1));
tempDates.push(...await fetchDates(2));
dates.value = tempDates;
console.log(dates.value);
}
async function fetchDates(offset : number) {
const date = new Date(Date.now());
const urlString = "/kalendarz/" + date.getFullYear() + '-' + Number(date.getMonth() + offset);
const response = await axiosInstance.get(urlString, {withCredentials: true});
const datesTemp :Array<Date> = response.data;
return datesTemp;
}
async function loadOrder(uuidString: string, confirmed: boolean, contractor: Ref<Contractor|undefined>, contractors: Ref<Array<Contractor>>, categories: Ref<Array<Category>>, route: Ref<Route|undefined>, routes: Ref<Array<Route>|undefined>) {
const response = await axiosInstance.get('/zamowienie/' + uuidString);
const tempOrder = response.data;
console.log(tempOrder);
if(confirmed) {
tempOrder.MZN_Bufor = 0;
}
contractor.value = <Contractor>contractors.value?.find((contractor) => contractor.Knt_KntId == tempOrder.MZN_PodID);
route.value = <Route>routes.value?.find((route) => route.MZT_MZTID == tempOrder.MZN_MZTID);
deliveryDate.value = new Date(tempOrder.MZN_DataDos);
orderDate.value = new Date(tempOrder.MZN_DataZam);
uuid.value = uuidString;
order.value = tempOrder;
additionalNotes.value = tempOrder.MZN_Uwagi;
if(categories.value == undefined) {
return;
}
for(const orderProduct of tempOrder.MZamElem){
for(const category of categories.value) {
const product = category.Towary.find(product => (product.Twr_TwrId == orderProduct.MZE_TwrId));
if(product != undefined && orderProduct.MZE_TwrCena != null) {
console.log(product);
if(orderProduct.MZE_TwrJm == product.Twr_JM) {
product.Twr_Cena = orderProduct.MZE_TwrCena.slice(0, -2);
} else if(orderProduct.Twr_Cena == product.Twr_JMZ) {
product.Twr_CenaZ = orderProduct.MZE_TwrCena.slice(0, -2);
}
product.Quantity = orderProduct.MZE_TwrIlosc.slice(0, -2);
product.ChosenOption = orderProduct.MZE_TwrJm;
category.isVisible = true;
break;
}
}
}
}
return {orders, order, uuid, deliveryDate, orderDate, dates, additionalNotes, fetchOrders, loadOrder, fetchOrdersByDay, fetchOrdersByBuffer, fetchOrdersByDateStartAndEnd, fetchDates, getOrderDates}
})

View File

@@ -0,0 +1,17 @@
import { defineStore } from 'pinia'
import type { Route } from '@/main'
import { ref } from 'vue'
import { axiosInstance } from '@/main'
export const useRoutesStore = defineStore('routes', () => {
const routes = ref<Array<Route>>();
const route = ref<Route>();
async function fetchRoutes() {
const response = await axiosInstance.get('/trasy', {withCredentials: true});
routes.value = [];
routes.value.push(...response.data);
}
return {routes, route, fetchRoutes};
});

View File

@@ -0,0 +1,83 @@
import { defineStore, storeToRefs } from 'pinia'
import { ref } from 'vue'
import { useOrdersStore } from '@/stores/orders.store'
import type { Order } from '@/main'
import { useContractorsStore } from '@/stores/contractors.store'
import { useCategoriesStore } from '@/stores/categories.store'
import { router } from '@/router/router'
import { useRoutesStore } from '@/stores/routes.store'
export const useSiteControlStore = defineStore('siteControl', () => {
const shownComponent = ref<string>("mainForm");
const showConfirmationModal = ref<boolean>(false);
const showCancellationModal = ref<boolean>(false);
const isDarkTheme = ref<boolean>(false);
const isLoading = ref<boolean>(true);
async function switchToForm() {
await router.push("/");
}
async function switchToOrders() {
await router.push("/orders");
// const orderStore = useOrdersStore();
// const { orders } = storeToRefs(orderStore);
// isLoading.value = true;
// const date = new Date(Date.now());
// const startDate = new Date(date.getFullYear(), date.getMonth(), (date.getDate() - 2));
// const endDate = new Date(date.getFullYear()+1, date.getMonth(), date.getDay());
// orders.value = await orderStore.fetchOrdersByDateStartAndEnd(startDate, endDate, null);
// console.log(orders.value);
// isLoading.value = false;
}
async function switchToTable() {
// const ordersStore = useOrdersStore();
// const { orders } = storeToRefs(ordersStore);
// orders.value = await ordersStore.fetchOrdersByDay(new Date(Date.now()), null);
await router.push("/summary");
}
function checkTheme() {
isDarkTheme.value = !!window?.matchMedia?.('(prefers-color-scheme:dark)')?.matches;
}
async function viewOrder(uuid : string) {
const orderStore = useOrdersStore();
const contractorsStore = useContractorsStore();
const categoriesStore = useCategoriesStore();
const routeStore = useRoutesStore();
shownComponent.value = "mainForm";
isLoading.value = true;
await categoriesStore.fetchCategories();
const { contractor, contractors } = storeToRefs(contractorsStore);
const { categories } = storeToRefs(categoriesStore);
const { route, routes } = storeToRefs( routeStore );
await orderStore.loadOrder(uuid, false, contractor, contractors, categories, route, routes);
isLoading.value=false;
await router.push("/");
}
async function newOrder(redirect : boolean) {
const ordersStore = useOrdersStore();
const contractorsStore = useContractorsStore();
const categoriesStore = useCategoriesStore();
const routeStore = useRoutesStore();
const { order, uuid, deliveryDate, orderDate } = storeToRefs(ordersStore);
const { contractor } = storeToRefs(contractorsStore);
const { route } = storeToRefs(routeStore);
contractor.value = undefined;
order.value = undefined;
uuid.value = undefined;
deliveryDate.value = undefined;
orderDate.value = undefined;
route.value = undefined;
await categoriesStore.fetchCategories();
if (redirect) {
await router.push("/");
}
}
return {isLoading, showConfirmationModal, showCancellationModal, isDarkTheme, shownComponent, switchToForm, switchToOrders, switchToTable, checkTheme, viewOrder, newOrder};
})

10
src/stores/user.store.ts Normal file
View File

@@ -0,0 +1,10 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore('user', () => {
const username = ref<string>();
const logoutButtonText = ref<string>();
return {username, logoutButtonText};
})

78
src/views/LoginView.vue Normal file
View File

@@ -0,0 +1,78 @@
<script setup lang="ts">
import { Form, Field } from 'vee-validate';
import * as Yup from 'yup';
import { axiosInstance } from '@/main'
import { router } from '@/router/router'
import { useUserStore } from '@/stores/user.store'
import { getActivePinia, storeToRefs } from 'pinia'
import { useCategoriesStore } from '@/stores/categories.store'
import { useSiteControlStore } from '@/stores/siteControl.store'
const schema = Yup.object().shape({
username: Yup.string().required('Nazwa użytkownika jest wymagana'),
password: Yup.string().required('Hasło jest wymagane')
});
async function onSubmit(values : any, { setErrors } : any) {
const { username, password } = values;
const body = await axiosInstance.post('/login', {
username: username,
password: password
}).catch ((error) => {
if(error.response.status == 401) {
setErrors({ apiError: "unauthorized" })
}
});
if(body != undefined && body.status == 200) {
const userStore = useUserStore();
const siteControlStore = useSiteControlStore();
const { username } = storeToRefs(userStore);
username.value = body.data.displayName;
await siteControlStore.newOrder(true);
}
}
</script>
<template>
<div class="container is-flex is-justify-content-space-evenly is-align-items-center" style="min-height: 100vh;">
<div class="box" style="width: 75%">
<h1 class="title is-3 mb-3">Login</h1>
<Form @submit="onSubmit" :validation-schema="schema" v-slot="{ errors, isSubmitting }">
<div class="form-group">
<div class="field mb-3">
<label class="label is-small">Nazwa użytkownika</label>
<div class="field is-small mb-3">
<Field class="input is-small"
type="text"
name="username"
:class="{ 'is-danger': errors.username }"/>
</div>
</div>
<div class="has-text-danger">{{errors.username}}</div>
</div>
<div class="form-group">
<div class="field mb-3">
<label class="label is-small">Hasło</label>
<div class="field is-small mb-3">
<Field class="input is-small"
type="password"
name="password"
:class="{ 'is-danger': errors.password }"/>
</div>
</div>
<div class="has-text-danger">{{errors.password}}</div>
</div>
<div class="form-group">
<button class="button is-info" :disabled="isSubmitting">
<span v-show="isSubmitting" class="spinner-border spinner-border-sm mr-1"></span>
Login
</button>
</div>
<div v-if="errors.apiError" class="has-text-danger mt-3 mb-3">Hasło lub nazwa użytkownika jest niepoprawna.</div>
</Form>
</div>
</div>
</template>

64
src/views/MainView.vue Normal file
View File

@@ -0,0 +1,64 @@
<template class="has-navbar-fixed-top">
<NavBar/>
<div v-if="isLoading">
<LoadingComponent/>
</div>
<div v-else>
<MainForm
v-if="order == undefined || order.MZN_Bufor==1"
/>
<ConfirmedForm v-else-if="order.MZN_Bufor==0"/>
</div>
<ConfirmationModal v-show="showConfirmationModal" @close="showConfirmationModal = false"></ConfirmationModal>
<CancelationModal v-show="showCancellationModal" @close="showCancellationModal = false"></CancelationModal>
<!-- <button class="button" @click="test">test</button>-->
</template>
<script setup lang="ts">
import '@/assets/base.css'
import { useContractorsStore } from '@/stores/contractors.store'
import { getActivePinia, storeToRefs } from 'pinia'
import { useCategoriesStore } from '@/stores/categories.store'
import { useOrdersStore } from '@/stores/orders.store'
import { useSiteControlStore } from '@/stores/siteControl.store'
import { useUserStore } from '@/stores/user.store'
import { useRoutesStore } from '@/stores/routes.store'
const contractorsStore = useContractorsStore();
const categoriesStore = useCategoriesStore();
const ordersStore = useOrdersStore();
const siteControlStore = useSiteControlStore();
const userStore = useUserStore();
const routesStore = useRoutesStore();
const contractors = storeToRefs(contractorsStore).contractors;
const contractor = storeToRefs(contractorsStore).contractor;
const categories = storeToRefs(categoriesStore).categories;
const { uuid, order } = storeToRefs(ordersStore);
const { username } = storeToRefs(userStore);
const { showConfirmationModal, showCancellationModal, isLoading, shownComponent } = storeToRefs(siteControlStore);
async function fetchData() {
//await categoriesStore.fetchCategories();
await contractorsStore.fetchContractors();
await routesStore.fetchRoutes();
//siteControlStore.newOrder(false);
isLoading.value = false;
}
siteControlStore.checkTheme();
fetchData();
</script>
<style>
@media screen and (min-width: 500px) {
.box {
--bulma-box-padding: 1.5rem;
}
}
@media screen and (max-width: 500px) {
.box {
--bulma-box-padding: 0.75rem;
}
}
</style>

12
src/views/OrdersView.vue Normal file
View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
</script>
<template>
<NavBar/>
<OrdersSelector class="box is-shadowless"/>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,8 @@
<script setup lang="ts">
import SummaryComponent from '@/components/SummaryComponent.vue'
</script>
<template>
<NavBar/>
<SummaryComponent/>
</template>

4
src/views/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export { default as LoginView } from "./LoginView.vue";
export { default as MainView } from "./MainView.vue";
export { default as SummaryView } from "./SummaryView.vue"
export { default as OrdersView } from "./OrdersView.vue"

30
srg
View File

@@ -1,30 +0,0 @@
<div class="columns is-mobile">
<div class="column">
<div class="field">
<input
class="input is-normal"
type="text"
placeholder="Kwota"
/>
</div>
</div>
<div class="column">
<div class="field has-addons">
<p class="control">
<span class="select is-small">
<select>
<option v-for="option in towar.Options" :key="option">{{option}}</option>
</select>
</span>
</p>
<p class="control is-expanded">
<input class="input" type="text" placeholder="Amount of money">
</p>
<p class="control">
<button class="button">
Transfer
</button>
</p>
</div>
</div>
</div>

View File

@@ -7,5 +7,8 @@
{
"path": "./tsconfig.app.json"
}
]
],
"compilerOptions": {
"allowJs": true
}
}

View File

@@ -2,11 +2,18 @@ import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite';
import {PrimeVueResolver} from '@primevue/auto-import-resolver';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [
PrimeVueResolver()
]
})
],
resolve: {
alias: {