Compare commits
	
		
			5 Commits
		
	
	
		
			cf2049798a
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c0aac3e49c | |||
| 182443530a | |||
| d62741e139 | |||
| 8426bd1ae8 | |||
| b18296f8a8 | 
| @@ -7,7 +7,8 @@ | |||||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0"> |   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|   <title>Odoo Hours</title> |   <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"> --> | ||||||
|   <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"> --> | ||||||
|  |   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | ||||||
| </head> | </head> | ||||||
|  |  | ||||||
| <body> | <body> | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										44
									
								
								app/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -9,8 +9,10 @@ | |||||||
|       "version": "0.0.0", |       "version": "0.0.0", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "bootstrap": "^5.3.3", |         "bootstrap": "^5.3.3", | ||||||
|  |         "chart.js": "^4.4.8", | ||||||
|         "pinia": "^3.0.1", |         "pinia": "^3.0.1", | ||||||
|         "vue": "^3.5.13", |         "vue": "^3.5.13", | ||||||
|  |         "vue-chartjs": "^5.3.2", | ||||||
|         "vue-router": "^4.5.0" |         "vue-router": "^4.5.0" | ||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
| @@ -298,14 +300,14 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/helpers": { |     "node_modules/@babel/helpers": { | ||||||
|       "version": "7.26.9", |       "version": "7.26.10", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", |       "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", | ||||||
|       "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", |       "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@babel/template": "^7.26.9", |         "@babel/template": "^7.26.9", | ||||||
|         "@babel/types": "^7.26.9" |         "@babel/types": "^7.26.10" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
| @@ -476,9 +478,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/types": { |     "node_modules/@babel/types": { | ||||||
|       "version": "7.26.9", |       "version": "7.26.10", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", |       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", | ||||||
|       "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", |       "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@babel/helper-string-parser": "^7.25.9", |         "@babel/helper-string-parser": "^7.25.9", | ||||||
| @@ -965,6 +967,12 @@ | |||||||
|         "@jridgewell/sourcemap-codec": "^1.4.14" |         "@jridgewell/sourcemap-codec": "^1.4.14" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@kurkle/color": { | ||||||
|  |       "version": "0.3.4", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", | ||||||
|  |       "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/@polka/url": { |     "node_modules/@polka/url": { | ||||||
|       "version": "1.0.0-next.28", |       "version": "1.0.0-next.28", | ||||||
|       "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", |       "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", | ||||||
| @@ -1631,6 +1639,18 @@ | |||||||
|       ], |       ], | ||||||
|       "license": "CC-BY-4.0" |       "license": "CC-BY-4.0" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/chart.js": { | ||||||
|  |       "version": "4.4.8", | ||||||
|  |       "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", | ||||||
|  |       "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@kurkle/color": "^0.3.0" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "pnpm": ">=8" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/convert-source-map": { |     "node_modules/convert-source-map": { | ||||||
|       "version": "2.0.0", |       "version": "2.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", |       "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", | ||||||
| @@ -2759,6 +2779,16 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/vue-chartjs": { | ||||||
|  |       "version": "5.3.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz", | ||||||
|  |       "integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "chart.js": "^4.1.1", | ||||||
|  |         "vue": "^3.0.0-0 || ^2.7.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/vue-router": { |     "node_modules/vue-router": { | ||||||
|       "version": "4.5.0", |       "version": "4.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", |       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", | ||||||
|   | |||||||
| @@ -10,8 +10,10 @@ | |||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "bootstrap": "^5.3.3", |     "bootstrap": "^5.3.3", | ||||||
|  |     "chart.js": "^4.4.8", | ||||||
|     "pinia": "^3.0.1", |     "pinia": "^3.0.1", | ||||||
|     "vue": "^3.5.13", |     "vue": "^3.5.13", | ||||||
|  |     "vue-chartjs": "^5.3.2", | ||||||
|     "vue-router": "^4.5.0" |     "vue-router": "^4.5.0" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|   | |||||||
							
								
								
									
										89
									
								
								app/src/components/BarChartComponent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/src/components/BarChartComponent.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | <template> | ||||||
|  |     <div class="chart-container"> | ||||||
|  |         <Bar :data="chartData" :options="chartOptions" :plugins="[chartOptions.plugins.customLabels]" /> | ||||||
|  |     </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { Bar } from 'vue-chartjs' | ||||||
|  | import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js' | ||||||
|  |  | ||||||
|  | // Rejestracja wymaganych komponentów Chart.js | ||||||
|  | ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale) | ||||||
|  |  | ||||||
|  | // Dane wykresu z dwiema seriami, które będą układane jedna nad drugą | ||||||
|  | const chartData = { | ||||||
|  |     labels: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec'], | ||||||
|  |     datasets: [ | ||||||
|  |         { | ||||||
|  |             label: 'Sprzedaż Online', | ||||||
|  |             data: [30, 40, 50, 60, 70], | ||||||
|  |             backgroundColor: 'rgba(54, 162, 235, 0.7)', | ||||||
|  |             stack: 'stack1', | ||||||
|  |             // borderWidth: 5, | ||||||
|  |             // borderRadius: 15, | ||||||
|  |             // borderSkipped: false, | ||||||
|  |             // borderColor: 'rgba(64, 148, 204, 0.7)', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             label: 'Sprzedaż Stacjonarna', | ||||||
|  |             data: [20, 30, 40, 50, -50], | ||||||
|  |             backgroundColor: 'rgba(255, 99, 132, 0.7)', | ||||||
|  |             stack: 'stack1', | ||||||
|  |             // borderWidth: 5, | ||||||
|  |             // borderRadius: 15, | ||||||
|  |             // borderSkipped: false, | ||||||
|  |             // borderColor: 'rgba(64, 148, 204, 0.7)', | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Konfiguracja wykresu – włączenie stacking | ||||||
|  | const chartOptions = { | ||||||
|  |     responsive: true, | ||||||
|  |     maintainAspectRatio: false, | ||||||
|  |     plugins: { | ||||||
|  |         legend: { position: 'top' }, | ||||||
|  |         tooltip: { enabled: true }, | ||||||
|  |         // Własny plugin do wyświetlania sumy wartości nad słupkami | ||||||
|  |         customLabels: { | ||||||
|  |             id: 'customLabels', | ||||||
|  |             afterDatasetsDraw(chart) { | ||||||
|  |                 const { ctx, data, scales: { x, y } } = chart | ||||||
|  |  | ||||||
|  |                 ctx.save() | ||||||
|  |                 ctx.font = 'bold 12px Arial' | ||||||
|  |                 ctx.fillStyle = 'black' | ||||||
|  |                 ctx.textAlign = 'center' | ||||||
|  |  | ||||||
|  |                 data.labels.forEach((label, index) => { | ||||||
|  |                     const total = data.datasets.reduce((sum, dataset) => sum + dataset.data[index], 0) // Oblicza sumę wartości dla danej kategorii | ||||||
|  |                     const barX = x.getPixelForValue(index) | ||||||
|  |                     const barY = y.getPixelForValue(total) - 10 // Umieszcza tekst nad słupkiem | ||||||
|  |                     ctx.fillText(total, barX, barY) | ||||||
|  |                 }) | ||||||
|  |  | ||||||
|  |                 ctx.restore() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     scales: { | ||||||
|  |         x: { | ||||||
|  |             stacked: true, | ||||||
|  |             grid: { display: false } | ||||||
|  |         }, | ||||||
|  |         y: { | ||||||
|  |             stacked: true, | ||||||
|  |             // grid: { display: false } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | .chart-container { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 200px; | ||||||
|  |     /* Ustawienie wysokości */ | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @@ -38,26 +38,26 @@ const props = defineProps({ | |||||||
| const emit = defineEmits(["update:hour", "update:minute"]); | const emit = defineEmits(["update:hour", "update:minute"]); | ||||||
| const interval = ref(null); | const interval = ref(null); | ||||||
| const timeout = 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 | const tempHour = ref(String(props.hour).padStart(2, "0")); | ||||||
|  | const tempMinute = ref(String(props.minute).padStart(2, "0")); | ||||||
|  |  | ||||||
|  |  | ||||||
| watch(() => props.hour, (newVal) => { | watch(() => props.hour, (newVal) => { | ||||||
|     tempHour.value = newVal; |     tempHour.value = String(newVal).padStart(2, "0"); | ||||||
|     console.log("watch") |     console.log("watch") | ||||||
| }); | }); | ||||||
| watch(() => props.minute, (newVal) => { | watch(() => props.minute, (newVal) => { | ||||||
|     tempMinute.value = newVal; |     tempMinute.value = String(newVal).padStart(2, "0"); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| // Funkcje walidacyjne |  | ||||||
| const validateHour = () => { | const validateHour = () => { | ||||||
|     const hour = Number(tempHour.value); |     const hour = Number(tempHour.value); | ||||||
|     if (!isNaN(hour) && hour >= 6 && hour <= 18) { |     if (!isNaN(hour) && hour >= 6 && hour <= 18) { | ||||||
|         emit("update:hour", hour); |         emit("update:hour", hour); | ||||||
|     } else { |     } else { | ||||||
|         tempHour.value = props.hour; // Przywróć poprzednią wartość |         tempHour.value = props.hour; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -65,41 +65,35 @@ const validateHour = () => { | |||||||
|  |  | ||||||
| const validateMinute = () => { | const validateMinute = () => { | ||||||
|     const minute = Number(tempMinute.value); |     const minute = Number(tempMinute.value); | ||||||
|     if (!isNaN(minute) && minute >= 0 && minute <= 59) { |     if (!isNaN(minute) && minute >= 0 && minute < 60) { | ||||||
|         emit("update:minute", minute); |         emit("update:minute", minute); | ||||||
|     } else { |     } else { | ||||||
|         tempMinute.value = props.minute; // Przywróć poprzednią wartość |         tempMinute.value = props.minute; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const incrementValue = (type) => { | const incrementValue = (type) => { | ||||||
|     if (type == "hour") { |     if (type == "hour") { | ||||||
|         let newHour = tempHour.value + 1; |         let newHour = props.hour + 1; | ||||||
|         if (newHour >= 6 && newHour <= 18) { |         if (newHour >= 6 && newHour <= 18) { | ||||||
|             // tempHour.value = newHour; |  | ||||||
|             emit("update:hour", newHour); |             emit("update:hour", newHour); | ||||||
|         } |         } | ||||||
|         // validateHour(); |  | ||||||
|     } else if (type === "minute") { |     } else if (type === "minute") { | ||||||
|         let newMinute = tempMinute.value + 1; |         let newMinute = (props.minute + 1) % 60; | ||||||
|         if (newMinute >= 0 && newMinute <= 59) { |  | ||||||
|         emit("update:minute", newMinute); |         emit("update:minute", newMinute); | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| const decrementValue = (type) => { | const decrementValue = (type) => { | ||||||
|     if (type == "hour") { |     if (type == "hour") { | ||||||
|         let newHour = tempHour.value - 1; |         let newHour = props.hour - 1; | ||||||
|         if (newHour >= 6 && newHour <= 18) { |         if (newHour >= 6 && newHour <= 18) { | ||||||
|             emit("update:hour", newHour); |             emit("update:hour", newHour); | ||||||
|         } |         } | ||||||
|     } else if (type === "minute") { |     } else if (type === "minute") { | ||||||
|         let newMinute = tempMinute.value - 1; |         let newMinute = (props.minute + 59) % 60; | ||||||
|         if (newMinute >= 0 && newMinute <= 59) { |  | ||||||
|         emit("update:minute", newMinute); |         emit("update:minute", newMinute); | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
| const onIncrementClick = (type) => { | const onIncrementClick = (type) => { | ||||||
|     incrementValue(type); |     incrementValue(type); | ||||||
| @@ -107,7 +101,7 @@ const onIncrementClick = (type) => { | |||||||
|         interval.value = setInterval(() => { |         interval.value = setInterval(() => { | ||||||
|             incrementValue(type); |             incrementValue(type); | ||||||
|         }, 100); |         }, 100); | ||||||
|     }, 1000); |     }, 500); | ||||||
|  |  | ||||||
| } | } | ||||||
| const onDecrementClick = (type) => { | const onDecrementClick = (type) => { | ||||||
| @@ -116,7 +110,7 @@ const onDecrementClick = (type) => { | |||||||
|         interval.value = setInterval(() => { |         interval.value = setInterval(() => { | ||||||
|             decrementValue(type); |             decrementValue(type); | ||||||
|         }, 100); |         }, 100); | ||||||
|     }, 1000); |     }, 500); | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -129,10 +123,6 @@ const stopInterval = () => { | |||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style scoped> | <style scoped> | ||||||
| button { |  | ||||||
|     width: 2.5rem; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .visibility-hidden { | .visibility-hidden { | ||||||
|     visibility: hidden; |     visibility: hidden; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -48,6 +48,7 @@ export const useWorkHoursStore = defineStore('workHours', () => { | |||||||
|   watch( |   watch( | ||||||
|     () => workHours.value.map((entry, index) => ({ |     () => workHours.value.map((entry, index) => ({ | ||||||
|       index, |       index, | ||||||
|  |       date: entry.date, | ||||||
|       enterHour: entry.enterHour, |       enterHour: entry.enterHour, | ||||||
|       enterMinute: entry.enterMinute, |       enterMinute: entry.enterMinute, | ||||||
|       leaveHour: entry.leaveHour, |       leaveHour: entry.leaveHour, | ||||||
| @@ -55,10 +56,15 @@ export const useWorkHoursStore = defineStore('workHours', () => { | |||||||
|     })), |     })), | ||||||
|     (newVal, oldVal) => { |     (newVal, oldVal) => { | ||||||
|       newVal.forEach((newEntry, index) => { |       newVal.forEach((newEntry, index) => { | ||||||
|         const oldEntry = oldVal[index]; |         const oldEntry = oldVal?.[index]; | ||||||
|  | 
 | ||||||
|  |         if (!oldEntry) { | ||||||
|  |           calculateWorkTime(workHours.value[index]); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         // Sprawdzamy, czy cokolwiek się zmieniło
 |         // Sprawdzamy, czy cokolwiek się zmieniło
 | ||||||
|         if ( |         else if ( | ||||||
|  |           newEntry.date !== oldEntry.date || | ||||||
|           newEntry.enterHour !== oldEntry.enterHour || |           newEntry.enterHour !== oldEntry.enterHour || | ||||||
|           newEntry.enterMinute !== oldEntry.enterMinute || |           newEntry.enterMinute !== oldEntry.enterMinute || | ||||||
|           newEntry.leaveHour !== oldEntry.leaveHour || |           newEntry.leaveHour !== oldEntry.leaveHour || | ||||||
| @@ -81,19 +87,71 @@ export const useWorkHoursStore = defineStore('workHours', () => { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const getWeekday = (index) => { |   const getWeekday = (index) => { | ||||||
|     const dayNames = ['NIE', 'PON', 'WTO', 'śRO', 'CZW', 'PIĄ', 'SOB']; |     const dayNames = ['NIE', 'PON', 'WTO', 'ŚRO', 'CZW', 'PIĄ', 'SOB']; | ||||||
|     const weekday = workHours.value[index].date.getDay(); |     const weekday = workHours.value[index].date.getDay(); | ||||||
|     return [weekday, dayNames[weekday]]; |     return [weekday, dayNames[weekday]]; | ||||||
|   } |   }; | ||||||
| 
 | 
 | ||||||
|   const clear = () => { |   const clear = () => { | ||||||
|     workHours.value = []; |     workHours.value = []; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const parseWorkHours = (userInput) => { | ||||||
|  |     const lines = userInput.split('\n').map(line => line.trim()).filter(line => line); | ||||||
|  |     const result = []; | ||||||
|  | 
 | ||||||
|  |     // przykładowa linia userInput: Marcin Nowak	21.02.2025 07:06:59	21.02.2025 15:58:04	08:51
 | ||||||
|  |     // const timeRegex = /(\d{2}\.\d{2}\.\d{4})\s(\d{2}):(\d{2}):\d{2}\s\d{2}\.\d{2}\.\d{4}\s(\d{2}):(\d{2}):\d{2}\s(\d{2}):(\d{2})/;
 | ||||||
|  |     const timeRegex = /(\d{2})\.(\d{2})\.(\d{4})\s(\d{2}):(\d{2}):\d{2}\s\d{2}\.\d{2}\.\d{4}\s(\d{2}):(\d{2}):\d{2}\s(\d{2}):(\d{2})/; | ||||||
|  | 
 | ||||||
|  |     for (const line of lines) { | ||||||
|  |       const match = line.match(timeRegex); | ||||||
|  |       if (match) { | ||||||
|  |         const [_, day, month, year, enterHour, enterMinute, leaveHour, leaveMinute, workHours, workMinutes] = match; | ||||||
|  | 
 | ||||||
|  |         const workDate = new Date(year, month - 1, day); | ||||||
|  | 
 | ||||||
|  |         result.push({ | ||||||
|  |           date: workDate, | ||||||
|  |           enterHour: parseInt(enterHour), | ||||||
|  |           enterMinute: parseInt(enterMinute), | ||||||
|  |           leaveHour: parseInt(leaveHour), | ||||||
|  |           leaveMinute: parseInt(leaveMinute), | ||||||
|  |           workHours: null, | ||||||
|  |           workMinutes: null, | ||||||
|  |         }); | ||||||
|       } |       } | ||||||
|  |     } | ||||||
|  |     result.sort((a, b) => a.date - b.date); | ||||||
|  |     workHours.value = result; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const addWorkDay = (date) => { | ||||||
|  |     const newEntry = { | ||||||
|  |       date: date, | ||||||
|  |       enterHour: 7, | ||||||
|  |       enterMinute: 0, | ||||||
|  |       leaveHour: 15, | ||||||
|  |       leaveMinute: 15, | ||||||
|  |       workHours: null, | ||||||
|  |       workMinutes: null | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const index = workHours.value.findIndex((entry) => entry.date > newEntry.date); | ||||||
|  | 
 | ||||||
|  |     if (index === -1) { | ||||||
|  |       workHours.value.push(newEntry); | ||||||
|  |     } else { | ||||||
|  |       workHours.value.splice(index, 0, newEntry); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|   return { |   return { | ||||||
|     workHours, |     workHours, | ||||||
|     getDate, |     getDate, | ||||||
|     getWeekday, |     getWeekday, | ||||||
|     clear |     clear, | ||||||
|  |     parseWorkHours, | ||||||
|  |     addWorkDay, | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
| @@ -1,14 +1,15 @@ | |||||||
| <template> | <template> | ||||||
|     <div class="container text-center"> |     <div class="container text-center"> | ||||||
|         <div class="row align-items-center"> |         <div class="row align-items-center"> | ||||||
|             <!-- Przyciski --> |             <!-- buttons --> | ||||||
|             <div class="col-3"></div> |             <div class="col-3"></div> | ||||||
|             <div class="col d-flex justify-content-center align-items-center"> |             <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" |                 <button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal" | ||||||
|                     data-bs-target="#inputModal">WCZYTAJ</button> |                     data-bs-target="#inputModal">WCZYTAJ</button> | ||||||
|                 <button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal" |                 <button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal" | ||||||
|                     data-bs-target="#clearAllModal">USUŃ</button> |                     data-bs-target="#clearAllModal">USUŃ</button> | ||||||
|                 <button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;">DODAJ</button> |                 <button type="button" class="btn btn-outline-primary mx-2" style="width: 150px;" data-bs-toggle="modal" | ||||||
|  |                     data-bs-target="#addModal">DODAJ</button> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <!-- Toggle --> |             <!-- Toggle --> | ||||||
| @@ -19,22 +20,6 @@ | |||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </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> | ||||||
|     <div class="container-fluid "> |     <div class="container-fluid "> | ||||||
|         <div class="text-center"> |         <div class="text-center"> | ||||||
| @@ -59,46 +44,16 @@ | |||||||
|                                     {{ storeWorkHours.getWeekday(index)[1] }}</span> |                                     {{ storeWorkHours.getWeekday(index)[1] }}</span> | ||||||
|                             </td> |                             </td> | ||||||
|                             <td class="align-middle"> |                             <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" |                                 <TimeInputComponent v-model:hour="day.enterHour" v-model:minute="day.enterMinute" | ||||||
|                                     :showButtons="showButtons" /> |                                     :showButtons="showButtons" /> | ||||||
|                             </td> |                             </td> | ||||||
|                             <td class="align-middle"> |                             <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" |                                 <TimeInputComponent v-model:hour="day.leaveHour" v-model:minute="day.leaveMinute" | ||||||
|                                     :showButtons="showButtons" /> |                                     :showButtons="showButtons" /> | ||||||
|                             </td> |                             </td> | ||||||
|                             <td class="align-middle"> |                             <td class="align-middle"> | ||||||
|                                 <div class="d-flex align-items-center justify-content-center"> |                                 <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> |                                     <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> |                                 </div> | ||||||
|                             </td> |                             </td> | ||||||
|                         </tr> |                         </tr> | ||||||
| @@ -154,50 +109,63 @@ | |||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  |     <!-- modal add entry --> | ||||||
|  |     <div class="modal fade" id="addModal" tabindex="-1" aria-labelledby="addModalLabel" aria-hidden="true"> | ||||||
|  |         <div class="modal-dialog"> | ||||||
|  |             <div class="modal-content"> | ||||||
|  |                 <div class="modal-header"> | ||||||
|  |                     <h1 class="modal-title fs-5" id="addModalLabel">Dodaj dzień roboczy</h1> | ||||||
|  |                     <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="modal-body"> | ||||||
|  |  | ||||||
|  |                     <label for="inputDate" class="form-label">wybierz date:</label> | ||||||
|  |                     <input type="date" id="inputDate" class="form-control" placeholder="dd-mm-yyyy" v-model="newDate"> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                 </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="!newDate" | ||||||
|  |                         @click="insertWorkDay">Dodaj dzień</button> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup> | <script setup> | ||||||
| import TimeInputComponent from "@/components/TimeInputComponent.vue"; | import TimeInputComponent from "@/components/TimeInputComponent.vue"; | ||||||
| import { useWorkHoursStore } from "../stores/counter.js"; | import { useWorkHoursStore } from "../stores/WorkHoursStore.js"; | ||||||
| import { Modal } from 'bootstrap'; |  | ||||||
| import { computed, watch, ref, onMounted } from "vue"; | import { computed, watch, ref, onMounted } from "vue"; | ||||||
|  |  | ||||||
| const rawText = ref(""); | const rawText = ref(""); | ||||||
| const storeWorkHours = useWorkHoursStore(); | const storeWorkHours = useWorkHoursStore(); | ||||||
| // const oldValue = ref(0); |  | ||||||
| const showButtons = ref(false); | const showButtons = ref(false); | ||||||
|  | const newDate = ref(null); | ||||||
|  |  | ||||||
| onMounted(() => { | onMounted(() => { | ||||||
|     const modalElement = document.getElementById('inputModal'); |     const inputModal = document.getElementById('inputModal'); | ||||||
|  |     const addModal = document.getElementById('addModal'); | ||||||
|  |  | ||||||
|     if (modalElement) { |     if (inputModal) { | ||||||
|         modalElement.addEventListener('hidden.bs.modal', () => { |         inputModal.addEventListener('hidden.bs.modal', () => { | ||||||
|             console.log('Modal został zamknięty kliknięciem poza oknem lub przez kliknięcie przycisku zamykania.'); |             rawText.value = ''; | ||||||
|  |         }); | ||||||
|             // Tutaj możesz dodać własną logikę |     } | ||||||
|             clearInput(); |     if (addModal) { | ||||||
|  |         addModal.addEventListener('hidden.bs.modal', () => { | ||||||
|  |             newDate.value = null; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
| const formattedWorkTime = (day) => { | const formattedWorkTime = (day) => { | ||||||
|     const hours = String(day.workHours).padStart(2, '0'); // Dodaj wiodące zero do godzin |     const hours = String(day.workHours).padStart(2, '0'); | ||||||
|     const minutes = String(day.workMinutes).padStart(2, '0'); // Dodaj wiodące zero do minut |     const minutes = String(day.workMinutes).padStart(2, '0'); | ||||||
|     return `${hours}:${minutes}`; // Zwróć sformatowany czas |     return `${hours}:${minutes}`; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // 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) => { | const getClassWeekday = (index) => { | ||||||
|     switch (storeWorkHours.getWeekday(index)[0]) { |     switch (storeWorkHours.getWeekday(index)[0]) { | ||||||
|         case 0: |         case 0: | ||||||
| @@ -210,20 +178,13 @@ const getClassWeekday = (index) => { | |||||||
| } | } | ||||||
|  |  | ||||||
| const processInputText = () => { | const processInputText = () => { | ||||||
|     console.log(rawText.value); |     storeWorkHours.parseWorkHours(rawText.value); | ||||||
| } | }; | ||||||
|  |  | ||||||
| const clearInput = () => { |  | ||||||
|     rawText.value = ''; |  | ||||||
| } |  | ||||||
| // const closeModal = () => { |  | ||||||
| //     const modalElement = document.getElementById('staticBackdrop'); |  | ||||||
| //     const modal = Modal.getInstance(modalElement); |  | ||||||
|  |  | ||||||
| //     if (modal) { |  | ||||||
| //         modal.hide(); |  | ||||||
| //     } |  | ||||||
| // } |  | ||||||
|  |  | ||||||
|  | const insertWorkDay = () => { | ||||||
|  |     const date = new Date(newDate.value); | ||||||
|  |     storeWorkHours.addWorkDay(date); | ||||||
|  |     newDate.value = null; | ||||||
|  | }; | ||||||
|  |  | ||||||
| </script> | </script> | ||||||
| @@ -1,7 +1,169 @@ | |||||||
| <template> | <template> | ||||||
|     <h1>Table</h1> |     <div class="container"> | ||||||
|  |         <div class="row gx-3"> | ||||||
|  |             <div class="col-4"> | ||||||
|  |                 <div class="card bg-left text-light h-100"> | ||||||
|  |                     <div class="row g-0 h-100"> | ||||||
|  |                         <div class="col-3 "> | ||||||
|  |                             <h2 class="rounded-start bg-light h-100 d-flex justify-content-center align-items-center"> | ||||||
|  |                                 <i class="text-left fa-solid fa-calendar"></i> | ||||||
|  |                             </h2> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="col-9 "> | ||||||
|  |                             <div class="card-body"> | ||||||
|  |                                 <h6 class="card-title">MIESIĄC:</h6> | ||||||
|  |                                 <h3 class="card-subtitle">Marzec</h3> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="col-4"> | ||||||
|  |                 <div class="card bg-mid text-light h-100"> | ||||||
|  |                     <div class="row g-0 "> | ||||||
|  |                         <div class="col-3 "> | ||||||
|  |                             <h2 class="rounded-start bg-light h-100 d-flex justify-content-center align-items-center"> | ||||||
|  |                                 <i class="text-mid fa-solid fa-hammer"></i> | ||||||
|  |                             </h2> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="col-9 "> | ||||||
|  |                             <div class="card-body"> | ||||||
|  |                                 <h6 class="card-title">ILOŚĆ DNI ROBOCZYCH:</h6> | ||||||
|  |                                 <h3 class="card-subtitle">21</h3> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="col-4"> | ||||||
|  |                 <div class="card bg-right text-light h-100"> | ||||||
|  |                     <div class="row g-0 "> | ||||||
|  |                         <div class="col-3 "> | ||||||
|  |                             <h2 class="rounded-start bg-light h-100 d-flex justify-content-center align-items-center"> | ||||||
|  |                                 <i class="text-right fa-solid fa-clock "></i> | ||||||
|  |                             </h2> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="col-9 "> | ||||||
|  |                             <div class="card-body"> | ||||||
|  |                                 <h6 class="card-title">WYMAGANA ILOŚĆ GODZIN:</h6> | ||||||
|  |                                 <h3 class="card-subtitle">168</h3> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="row gx-3 mt-3 text-center"> | ||||||
|  |             <div class="col-2"> | ||||||
|  |                 <div class="card h-100"> | ||||||
|  |                     <div class="card-header text-bg-primary">ILOŚĆ GODZIN</div> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <h3 class="card-title">182</h3> | ||||||
|  |                         <!-- <h6 class="card-text mb-2 ">Marzec 2025</h6> --> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="col-2"> | ||||||
|  |                 <div class="card h-100"> | ||||||
|  |                     <div class="card-header text-bg-primary">RÓŻNICA GODZIN</div> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <h5 class="card-title">+15</h5> | ||||||
|  |                         <!-- <h6 class="card-text mb-2 ">Marzec 2025</h6> --> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="col-2"> | ||||||
|  |                 <div class="card h-100"> | ||||||
|  |                     <div class="card-header text-bg-primary">PROGRES</div> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <h5 class="card-title">18/21</h5> | ||||||
|  |                         <!-- <h6 class="card-text mb-2 ">Marzec 2025</h6> --> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="col-2"> | ||||||
|  |                 <div class="card h-100"> | ||||||
|  |                     <div class="card-header text-bg-primary">DNI URLOPU</div> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <h5 class="card-title">0</h5> | ||||||
|  |                         <!-- <h6 class="card-text mb-2 ">Marzec 2025</h6> --> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="col-2"> | ||||||
|  |                 <div class="card h-100"> | ||||||
|  |                     <div class="card-header text-bg-primary">DNI CHOROBOWEGO</div> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <h5 class="card-title">0</h5> | ||||||
|  |                         <!-- <h6 class="card-text mb-2 ">Marzec 2025</h6> --> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="col-2"> | ||||||
|  |                 <div class="card h-100"> | ||||||
|  |                     <div class="card-header text-bg-primary">DNI ŚWIĄTECZNE</div> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <h5 class="card-title">1</h5> | ||||||
|  |                         <!-- <h6 class="card-text mb-2 ">Marzec 2025</h6> --> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <!-- <div class="d-flex justify-content-center g-1"> | ||||||
|  |             <div class="col-auto mx-auto"></div> | ||||||
|  |         </div> --> | ||||||
|  |         <div class="row gx-3"> | ||||||
|  |             <!-- <div class="col-auto"> --> | ||||||
|  |             <BarChartComponent /> | ||||||
|  |  | ||||||
|  |             <!-- </div> --> | ||||||
|  |  | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  |  | ||||||
| <script setup> | <script setup> | ||||||
|  | import BarChartComponent from '@/components/BarChartComponent.vue'; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | .bg-left { | ||||||
|  |     background: rgb(25, 135, 84); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-left { | ||||||
|  |     color: rgb(25, 135, 84); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-left-light { | ||||||
|  |     color: rgb(253, 126, 20); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .bg-mid { | ||||||
|  |     background: rgb(202, 101, 16); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-mid { | ||||||
|  |     color: rgb(202, 101, 16); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-mid-light { | ||||||
|  |     color: rgb(253, 126, 20); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .bg-right { | ||||||
|  |     background: rgb(10, 162, 192); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-right { | ||||||
|  |     color: rgb(10, 162, 192); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-right-light { | ||||||
|  |     color: rgb(253, 126, 20); | ||||||
|  | } | ||||||
|  | </style> | ||||||
		Reference in New Issue
	
	Block a user