editView almost done
This commit is contained in:
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
Dockerfile
|
||||||
|
docker-compose.yml
|
||||||
24
Dockerfile
Normal file
24
Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Budowanie aplikacji
|
||||||
|
FROM node:lts-alpine AS build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY ./app/package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY ./app .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Serwowanie za pomocą NGINX
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Skopiuj zbudowaną aplikację
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Skopiuj własną konfigurację NGINX
|
||||||
|
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
30
app/.gitignore
vendored
Normal file
30
app/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
3
app/.vscode/extensions.json
vendored
Normal file
3
app/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
||||||
29
app/README.md
Normal file
29
app/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# app
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||||
|
|
||||||
|
## Customize configuration
|
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
18
app/index.html
Normal file
18
app/index.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Odoo Hours</title>
|
||||||
|
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
8
app/jsconfig.json
Normal file
8
app/jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
2820
app/package-lock.json
generated
Normal file
2820
app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
app/package.json
Normal file
22
app/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "app",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
|
"pinia": "^3.0.1",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"vite": "^6.1.0",
|
||||||
|
"vite-plugin-vue-devtools": "^7.7.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
app/public/favicon.ico
Normal file
BIN
app/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
14
app/src/App.vue
Normal file
14
app/src/App.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<navbar></navbar>
|
||||||
|
<RouterView></RouterView>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { RouterLink, RouterView } from 'vue-router'
|
||||||
|
import navbar from './components/Navbar.vue'
|
||||||
|
</script>
|
||||||
1
app/src/assets/logo.svg
Normal file
1
app/src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||||
|
After Width: | Height: | Size: 276 B |
24
app/src/components/Navbar.vue
Normal file
24
app/src/components/Navbar.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-3">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- <a class="navbar-brand" href="#">Odoo Hours</a> -->
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup"
|
||||||
|
aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse justify-content-center" id="navbarNavAltMarkup">
|
||||||
|
<div class="navbar-nav">
|
||||||
|
<RouterLink class="nav-link" to="/" activeClass="active">Edycja</RouterLink>
|
||||||
|
<RouterLink class="nav-link" to="/table" activeClass="active">Tabela</RouterLink>
|
||||||
|
<RouterLink class="nav-link" to="/calendar" activeClass="active">Kalendarz</RouterLink>
|
||||||
|
<!-- <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { RouterLink, RouterView } from 'vue-router'
|
||||||
|
</script>
|
||||||
139
app/src/components/TimeInputComponent.vue
Normal file
139
app/src/components/TimeInputComponent.vue
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<template>
|
||||||
|
<div class="d-flex align-items-center justify-content-center me-4">
|
||||||
|
<!-- Godziny -->
|
||||||
|
<button class="btn btn-outline-secondary me-2 " tabindex="-1" @mousedown="onDecrementClick('hour')"
|
||||||
|
@mouseup="stopInterval" @mouseleave="stopInterval" :class="{ 'visibility-hidden': !showButtons }"><i
|
||||||
|
class="fa fa-minus" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary me-1" tabindex="-1" @mousedown="onIncrementClick('hour')"
|
||||||
|
@mouseup="stopInterval" @mouseleave="stopInterval" :class="{ 'visibility-hidden': !showButtons }">
|
||||||
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<input class="form-control text-center mx-1" style="width: 3rem;" type="text" v-model="tempHour"
|
||||||
|
@blur="validateHour" @keyup.enter="validateHour">
|
||||||
|
<span class="mx-1">:</span>
|
||||||
|
<!-- Minuty -->
|
||||||
|
<input class="form-control text-center mx-1" style="width: 3rem;" type="text" v-model="tempMinute"
|
||||||
|
@blur="validateMinute" @keyup.enter="validateMinute">
|
||||||
|
<button class="btn btn-outline-secondary ms-1" tabindex="-1" @mousedown="onDecrementClick('minute')"
|
||||||
|
@mouseup="stopInterval" @mouseleave="stopInterval" :class="{ 'visibility-hidden': !showButtons }">
|
||||||
|
<i class="fa fa-minus" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary ms-2" tabindex="-1" @mousedown="onIncrementClick('minute')"
|
||||||
|
@mouseup="stopInterval" @mouseleave="stopInterval" :class="{ 'visibility-hidden': !showButtons }">
|
||||||
|
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps, defineEmits, ref, watch, computed } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
hour: Number,
|
||||||
|
minute: Number,
|
||||||
|
showButtons: Boolean,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:hour", "update:minute"]);
|
||||||
|
const interval = ref(null);
|
||||||
|
const timeout = ref(null);
|
||||||
|
// Tymczasowe zmienne na input
|
||||||
|
const tempHour = ref(props.hour);
|
||||||
|
const tempMinute = ref(props.minute);
|
||||||
|
|
||||||
|
// Nasłuchiwanie zmian propsów, aby synchronizować z ref
|
||||||
|
watch(() => props.hour, (newVal) => {
|
||||||
|
tempHour.value = newVal;
|
||||||
|
console.log("watch")
|
||||||
|
});
|
||||||
|
watch(() => props.minute, (newVal) => {
|
||||||
|
tempMinute.value = newVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Funkcje walidacyjne
|
||||||
|
const validateHour = () => {
|
||||||
|
const hour = Number(tempHour.value);
|
||||||
|
if (!isNaN(hour) && hour >= 6 && hour <= 18) {
|
||||||
|
emit("update:hour", hour);
|
||||||
|
} else {
|
||||||
|
tempHour.value = props.hour; // Przywróć poprzednią wartość
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateMinute = () => {
|
||||||
|
const minute = Number(tempMinute.value);
|
||||||
|
if (!isNaN(minute) && minute >= 0 && minute <= 59) {
|
||||||
|
emit("update:minute", minute);
|
||||||
|
} else {
|
||||||
|
tempMinute.value = props.minute; // Przywróć poprzednią wartość
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const incrementValue = (type) => {
|
||||||
|
if (type == "hour") {
|
||||||
|
let newHour = tempHour.value + 1;
|
||||||
|
if (newHour >= 6 && newHour <= 18) {
|
||||||
|
// tempHour.value = newHour;
|
||||||
|
emit("update:hour", newHour);
|
||||||
|
}
|
||||||
|
// validateHour();
|
||||||
|
} else if (type === "minute") {
|
||||||
|
let newMinute = tempMinute.value + 1;
|
||||||
|
if (newMinute >= 0 && newMinute <= 59) {
|
||||||
|
emit("update:minute", newMinute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const decrementValue = (type) => {
|
||||||
|
if (type == "hour") {
|
||||||
|
let newHour = tempHour.value - 1;
|
||||||
|
if (newHour >= 6 && newHour <= 18) {
|
||||||
|
emit("update:hour", newHour);
|
||||||
|
}
|
||||||
|
} else if (type === "minute") {
|
||||||
|
let newMinute = tempMinute.value - 1;
|
||||||
|
if (newMinute >= 0 && newMinute <= 59) {
|
||||||
|
emit("update:minute", newMinute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onIncrementClick = (type) => {
|
||||||
|
incrementValue(type);
|
||||||
|
timeout.value = setTimeout(() => {
|
||||||
|
interval.value = setInterval(() => {
|
||||||
|
incrementValue(type);
|
||||||
|
}, 100);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
const onDecrementClick = (type) => {
|
||||||
|
decrementValue(type);
|
||||||
|
timeout.value = setTimeout(() => {
|
||||||
|
interval.value = setInterval(() => {
|
||||||
|
decrementValue(type);
|
||||||
|
}, 100);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopInterval = () => {
|
||||||
|
clearTimeout(timeout.value);
|
||||||
|
clearInterval(interval.value);
|
||||||
|
timeout.value = null;
|
||||||
|
interval.value = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
button {
|
||||||
|
width: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visibility-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
7
app/src/components/icons/IconCommunity.vue
Normal file
7
app/src/components/icons/IconCommunity.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
7
app/src/components/icons/IconDocumentation.vue
Normal file
7
app/src/components/icons/IconDocumentation.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
7
app/src/components/icons/IconEcosystem.vue
Normal file
7
app/src/components/icons/IconEcosystem.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
7
app/src/components/icons/IconSupport.vue
Normal file
7
app/src/components/icons/IconSupport.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
19
app/src/components/icons/IconTooling.vue
Normal file
19
app/src/components/icons/IconTooling.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
aria-hidden="true"
|
||||||
|
role="img"
|
||||||
|
class="iconify iconify--mdi"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
17
app/src/main.js
Normal file
17
app/src/main.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// import './assets/main.css'
|
||||||
|
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||||
|
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
|
||||||
|
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
app.use(createPinia())
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
35
app/src/router/index.js
Normal file
35
app/src/router/index.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import EditView from '../views/EditView.vue'
|
||||||
|
import TableView from '../views/TableView.vue'
|
||||||
|
import CalendarView from '../views/CalendarView.vue'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'edit',
|
||||||
|
component: EditView,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/table',
|
||||||
|
name: 'table',
|
||||||
|
component: TableView,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/calendar',
|
||||||
|
name: 'calendar',
|
||||||
|
component: CalendarView,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: '/about',
|
||||||
|
// name: 'about',
|
||||||
|
// // route level code-splitting
|
||||||
|
// // this generates a separate chunk (About.[hash].js) for this route
|
||||||
|
// // which is lazy-loaded when the route is visited.
|
||||||
|
// component: () => import('../views/AboutView.vue'),
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
99
app/src/stores/counter.js
Normal file
99
app/src/stores/counter.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useWorkHoursStore = defineStore('workHours', () => {
|
||||||
|
const workHours = ref([
|
||||||
|
{
|
||||||
|
date: new Date(2025, 1, 1),
|
||||||
|
enterHour: 7,
|
||||||
|
enterMinute: 20,
|
||||||
|
leaveHour: 15,
|
||||||
|
leaveMinute: 15,
|
||||||
|
workHours: 7,
|
||||||
|
workMinutes: 55,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: new Date(2025, 1, 2),
|
||||||
|
enterHour: 8,
|
||||||
|
enterMinute: 21,
|
||||||
|
leaveHour: 16,
|
||||||
|
leaveMinute: 16,
|
||||||
|
workHours: 7,
|
||||||
|
workMinutes: 55,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: new Date(2025, 1, 3),
|
||||||
|
enterHour: 9,
|
||||||
|
enterMinute: 22,
|
||||||
|
leaveHour: 17,
|
||||||
|
leaveMinute: 17,
|
||||||
|
workHours: 7,
|
||||||
|
workMinutes: 55,
|
||||||
|
},
|
||||||
|
|
||||||
|
]);
|
||||||
|
|
||||||
|
const calculateWorkTime = (entry) => {
|
||||||
|
let totalMinutes = (entry.leaveHour * 60 + entry.leaveMinute) - (entry.enterHour * 60 + entry.enterMinute);
|
||||||
|
|
||||||
|
if (totalMinutes < 0) {
|
||||||
|
totalMinutes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.workHours = Math.floor(totalMinutes / 60);
|
||||||
|
entry.workMinutes = totalMinutes % 60;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Automatyczne przeliczanie czasu pracy
|
||||||
|
watch(
|
||||||
|
() => workHours.value.map((entry, index) => ({
|
||||||
|
index,
|
||||||
|
enterHour: entry.enterHour,
|
||||||
|
enterMinute: entry.enterMinute,
|
||||||
|
leaveHour: entry.leaveHour,
|
||||||
|
leaveMinute: entry.leaveMinute,
|
||||||
|
})),
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
newVal.forEach((newEntry, index) => {
|
||||||
|
const oldEntry = oldVal[index];
|
||||||
|
|
||||||
|
// Sprawdzamy, czy cokolwiek się zmieniło
|
||||||
|
if (
|
||||||
|
newEntry.enterHour !== oldEntry.enterHour ||
|
||||||
|
newEntry.enterMinute !== oldEntry.enterMinute ||
|
||||||
|
newEntry.leaveHour !== oldEntry.leaveHour ||
|
||||||
|
newEntry.leaveMinute !== oldEntry.leaveMinute
|
||||||
|
) {
|
||||||
|
calculateWorkTime(workHours.value[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const getDate = (index) => {
|
||||||
|
const date = workHours.value[index].date; // Pobieramy datę
|
||||||
|
const day = String(date.getDate()).padStart(2, "0");
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const formattedDate = `${day}.${month}.${year}`;
|
||||||
|
return formattedDate;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWeekday = (index) => {
|
||||||
|
const dayNames = ['NIE', 'PON', 'WTO', 'śRO', 'CZW', 'PIĄ', 'SOB'];
|
||||||
|
const weekday = workHours.value[index].date.getDay();
|
||||||
|
return [weekday, dayNames[weekday]];
|
||||||
|
}
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
workHours.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
workHours,
|
||||||
|
getDate,
|
||||||
|
getWeekday,
|
||||||
|
clear
|
||||||
|
}
|
||||||
|
})
|
||||||
7
app/src/views/CalendarView.vue
Normal file
7
app/src/views/CalendarView.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<h1>Calendar</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
</script>
|
||||||
229
app/src/views/EditView.vue
Normal file
229
app/src/views/EditView.vue
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container text-center">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<!-- Przyciski -->
|
||||||
|
<div class="col-3"></div>
|
||||||
|
<div class="col d-flex justify-content-center align-items-center">
|
||||||
|
<button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#inputModal">WCZYTAJ</button>
|
||||||
|
<button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#clearAllModal">USUŃ</button>
|
||||||
|
<button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;">DODAJ</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toggle -->
|
||||||
|
<div class="col-3">
|
||||||
|
<div class="form-check form-switch d-flex justify-content-start align-items-center">
|
||||||
|
<input class="form-check-input p-2" type="checkbox" id="toggleSwitch" v-model="showButtons">
|
||||||
|
<label class="form-check-label p-2" for="toggleSwitch">Pokaż przyciski</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="d-flex flex-row justify-content-center align-items-center">
|
||||||
|
Przyciski
|
||||||
|
<div class="d-flex">
|
||||||
|
<button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#inputModal">WCZYTAJ</button>
|
||||||
|
<button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#clearAllModal">USUŃ</button>
|
||||||
|
<button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;">DODAJ</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Toggle
|
||||||
|
<div class="form-check form-switch ms-5">
|
||||||
|
<input class="form-check-input" type="checkbox" id="toggleSwitch">
|
||||||
|
<label class="form-check-label" for="toggleSwitch">Toggle</label>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid ">
|
||||||
|
<div class="text-center">
|
||||||
|
<table class="table w-50 mx-auto my-3">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center" scope="col">Data</th>
|
||||||
|
<th class="text-center" scope="col">Dzień</th>
|
||||||
|
<th class="text-center" scope="col">Wejście</th>
|
||||||
|
<th class="text-center" scope="col">Wyjście</th>
|
||||||
|
<th class="text-center" scope="col">Godziny</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<template v-for="(day, index) in storeWorkHours.workHours" :key="day.date">
|
||||||
|
<tr>
|
||||||
|
<td class="align-middle">
|
||||||
|
<div class="text-center">{{ storeWorkHours.getDate(index) }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="align-middle">
|
||||||
|
<span class="text-center badge mx-3" :class="getClassWeekday(index)">
|
||||||
|
{{ storeWorkHours.getWeekday(index)[1] }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="align-middle">
|
||||||
|
<!-- <div class="d-flex align-items-center justify-content-center">
|
||||||
|
Godziny dla Enter
|
||||||
|
<input class="form-control text-center" style="width: 4rem;" type="text"
|
||||||
|
v-model="day.enterHour" @focus="saveOldValue(day, 'enterHour')"
|
||||||
|
@blur="validate(day, 'enterHour', 6, 18, $event)">
|
||||||
|
<span class="mx-1">:</span>
|
||||||
|
Minuty dla Enter
|
||||||
|
<input class="form-control text-center" style="width: 4rem;" type="text"
|
||||||
|
v-model="day.enterMinute" @focus="saveOldValue(day, 'enterMinute')"
|
||||||
|
@blur="validate(day, 'enterMinute', 0, 59, $event)">
|
||||||
|
</div> -->
|
||||||
|
<TimeInputComponent v-model:hour="day.enterHour" v-model:minute="day.enterMinute"
|
||||||
|
:showButtons="showButtons" />
|
||||||
|
</td>
|
||||||
|
<td class="align-middle">
|
||||||
|
<!-- <div class="d-flex align-items-center justify-content-center">
|
||||||
|
Godziny dla Leave
|
||||||
|
<input class="form-control text-center" style="width: 4rem;" type="text"
|
||||||
|
v-model="day.leaveHour" @focus="saveOldValue(day, 'leaveHour')"
|
||||||
|
@blur="validate(day, 'leaveHour', 6, 18, $event)">
|
||||||
|
<span class="mx-1">:</span>
|
||||||
|
Minuty dla Leave
|
||||||
|
<input class="form-control text-center" style="width: 4rem;" type="text"
|
||||||
|
v-model="day.leaveMinute" @focus="saveOldValue(day, 'leaveMinute')"
|
||||||
|
@blur="validate(day, 'leaveMinute', 0, 59, $event)">
|
||||||
|
</div> -->
|
||||||
|
<TimeInputComponent v-model:hour="day.leaveHour" v-model:minute="day.leaveMinute"
|
||||||
|
:showButtons="showButtons" />
|
||||||
|
</td>
|
||||||
|
<td class="align-middle">
|
||||||
|
<div class="d-flex align-items-center justify-content-center">
|
||||||
|
<!-- Godziny dla Hours -->
|
||||||
|
<!-- <input class="form-control text-center" style="width: 4rem;" type="text"
|
||||||
|
v-model="day.workHours" @focus="saveOldValue(day, 'workHours')"
|
||||||
|
@blur="validate(day, 'workHours', 6, 18, $event)"> -->
|
||||||
|
<span class="mx-1 fs-5">{{ formattedWorkTime(day) }}</span>
|
||||||
|
<!-- Minuty dla Hours -->
|
||||||
|
<!-- <input class="form-control text-center" style="width: 4rem;" type="text"
|
||||||
|
v-model="day.workMinutes" @focus="saveOldValue(day, 'workMinutes')"
|
||||||
|
@blur="validate(day, 'workMinutes', 0, 59, $event)"> -->
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- modal input text -->
|
||||||
|
<div class="modal fade" id="inputModal" tabindex="-1" aria-labelledby="inputModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="inputModalLabel">Wklej swój tekst</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<!-- <label for="inputText" class="form-label">Wklej swój tekst:</label> -->
|
||||||
|
<textarea name="input_text" id="inputText" class="form-control" rows="10"
|
||||||
|
placeholder="Wpisz lub wklej tutaj swój tekst" v-model="rawText" required></textarea>
|
||||||
|
<div class="invalid-feedback">Pole tekstowe nie może być puste.</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Zamknij</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" :disabled="!rawText"
|
||||||
|
@click="processInputText">Przetwórz teskt</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- modal confirm clear -->
|
||||||
|
<div class="modal fade" id="clearAllModal" tabindex="-1" aria-labelledby="clearAllModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="clearAllModalLabel">Usuń wszystkie dane</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Czy jesteś pewien?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Nie</button>
|
||||||
|
<button type="button" class="btn btn-danger" data-bs-dismiss="modal"
|
||||||
|
@click="storeWorkHours.clear()">USUŃ</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import TimeInputComponent from "@/components/TimeInputComponent.vue";
|
||||||
|
import { useWorkHoursStore } from "../stores/counter.js";
|
||||||
|
import { Modal } from 'bootstrap';
|
||||||
|
import { computed, watch, ref, onMounted } from "vue";
|
||||||
|
|
||||||
|
const rawText = ref("");
|
||||||
|
const storeWorkHours = useWorkHoursStore();
|
||||||
|
// const oldValue = ref(0);
|
||||||
|
const showButtons = ref(false);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const modalElement = document.getElementById('inputModal');
|
||||||
|
|
||||||
|
if (modalElement) {
|
||||||
|
modalElement.addEventListener('hidden.bs.modal', () => {
|
||||||
|
console.log('Modal został zamknięty kliknięciem poza oknem lub przez kliknięcie przycisku zamykania.');
|
||||||
|
|
||||||
|
// Tutaj możesz dodać własną logikę
|
||||||
|
clearInput();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const formattedWorkTime = (day) => {
|
||||||
|
const hours = String(day.workHours).padStart(2, '0'); // Dodaj wiodące zero do godzin
|
||||||
|
const minutes = String(day.workMinutes).padStart(2, '0'); // Dodaj wiodące zero do minut
|
||||||
|
return `${hours}:${minutes}`; // Zwróć sformatowany czas
|
||||||
|
};
|
||||||
|
|
||||||
|
// function saveOldValue(item, field) {
|
||||||
|
// oldValue.value = item[field];
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const validate = (item, field, min, max, event) => {
|
||||||
|
// const newValue = Number(event.target.value);
|
||||||
|
// console.log("validate: " + field + ", min: " + min + ", max: " + max + ", newValue: " + newValue + ", oldValue: " + oldValue.value);
|
||||||
|
// if (isNaN(newValue) || newValue < min || newValue > max) {
|
||||||
|
// item[field] = oldValue.value; // Przywróć starą wartość
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
const getClassWeekday = (index) => {
|
||||||
|
switch (storeWorkHours.getWeekday(index)[0]) {
|
||||||
|
case 0:
|
||||||
|
return "text-bg-danger";
|
||||||
|
case 6:
|
||||||
|
return "text-bg-success";
|
||||||
|
default:
|
||||||
|
return "text-bg-secondary";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processInputText = () => {
|
||||||
|
console.log(rawText.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearInput = () => {
|
||||||
|
rawText.value = '';
|
||||||
|
}
|
||||||
|
// const closeModal = () => {
|
||||||
|
// const modalElement = document.getElementById('staticBackdrop');
|
||||||
|
// const modal = Modal.getInstance(modalElement);
|
||||||
|
|
||||||
|
// if (modal) {
|
||||||
|
// modal.hide();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
7
app/src/views/TableView.vue
Normal file
7
app/src/views/TableView.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<h1>Table</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
</script>
|
||||||
26
app/vite.config.js
Normal file
26
app/vite.config.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueDevTools(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
'bootstrap5': 'bootstrap/dist/css/bootstrap.min.css'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0', // Udostępnia serwer na zewnątrz kontenera (Docker)
|
||||||
|
port: 5172, // Zmiana portu na 5172
|
||||||
|
watch: {
|
||||||
|
usePolling: true // Poprawka dla Docker na Linuxie
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
12
docker-compose.dev.yml
Normal file
12
docker-compose.dev.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
vue-app-dev:
|
||||||
|
image: node:lts-alpine
|
||||||
|
working_dir: /app
|
||||||
|
volumes:
|
||||||
|
- ./app:/app
|
||||||
|
- /app/node_modules
|
||||||
|
ports:
|
||||||
|
- "5172:5172" # Vite domyślnie na 5173
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
command: sh -c "npm install && npm run dev"
|
||||||
12
docker-compose.yml
Normal file
12
docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
vue-app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./app/src:/app/src
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
restart: unless-stopped
|
||||||
35
nginx/default.conf
Normal file
35
nginx/default.conf
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name bartool.ovh;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
|
# Obsługa aplikacji pod ścieżką /vue-app
|
||||||
|
location /vue-app {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Przekierowanie dla Vue Router (SPA mode)
|
||||||
|
try_files $uri /index.html;
|
||||||
|
|
||||||
|
# Przepuszczenie nagłówków
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optymalizacja statycznych plików
|
||||||
|
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg)$ {
|
||||||
|
expires 6M;
|
||||||
|
access_log off;
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Kompresja
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/json;
|
||||||
|
gzip_min_length 1000;
|
||||||
|
|
||||||
|
# Logi
|
||||||
|
access_log /var/log/nginx/access.log;
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user