diff --git a/Layout/default/VueViews/WorkorderCompanyPWA.php b/Layout/default/VueViews/WorkorderCompanyPWA.php index 4c365e84e..774a6ceea 100644 --- a/Layout/default/VueViews/WorkorderCompanyPWA.php +++ b/Layout/default/VueViews/WorkorderCompanyPWA.php @@ -1,52 +1,3 @@ -performLogin($username, $password, $code2fa, $remember); - } - } - - $loginController = new PwaLoginController(); - - $username = $_POST['Username'] ?? ''; - $password = $_POST['Password'] ?? ''; - $twoFactorCode = isset($_POST['TwofactorCode']) && is_numeric($_POST['TwofactorCode']) ? (int)$_POST['TwofactorCode'] : 'unset'; - $remember = isset($_POST['Remember']) && $_POST['Remember'] === 'true'; - - $result = $loginController->apiLogin($username, $password, $twoFactorCode, $remember); - - $response = ['status' => 'error', 'message' => 'Anmeldung fehlgeschlagen.']; - - if ($result === true) { - $response = ['status' => 'success']; - } elseif ($result === '2fa') { - $response = ['status' => '2fa_required']; - } elseif ($result === 'false2fa') { - $response = ['status' => 'invalid_2fa', 'message' => 'Verifizierungscode falsch oder abgelaufen.']; - } else { - $response['message'] = 'Benutzername oder Passwort ist falsch.'; - } - - echo json_encode($response); - exit; // Stop execution to prevent rendering the full HTML page. -} - -// --- HTML & Vue App Rendering --- -?> @@ -83,7 +34,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ - +
@@ -159,7 +109,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ } } catch (error) { isAuthenticated.value = false; - console.error("Auth check failed:", error); } finally { isCheckingAuth.value = false; } @@ -169,7 +118,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ loginForm.isLoading = true; loginForm.error = ''; const formData = new FormData(); - formData.append('action', 'pwa_login'); formData.append('Username', loginForm.username); formData.append('Password', loginForm.password); formData.append('Remember', loginForm.remember); @@ -179,7 +127,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ } try { - const response = await axios.post(window.location.pathname, formData); + // Use the new, dedicated login endpoint + const response = await axios.post(`${API_BASE_URL}/loginOverride`, formData); const data = response.data; if (data.status === 'success') { @@ -193,8 +142,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ loginForm.state = 'credentials'; } } catch (error) { - loginForm.error = 'Ein Netzwerkfehler ist aufgetreten.'; - console.error('Login request failed:', error); + if(error.response && error.response.status === 403) { + loginForm.error = 'Sie sind bereits in einem anderen Tab angemeldet. Bitte laden Sie die Seite neu.'; + } else { + loginForm.error = 'Ein Netzwerkfehler ist aufgetreten.'; + } } finally { loginForm.isLoading = false; } @@ -203,16 +155,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ const handleLogout = async () => { isSettingsOpen.value = false; try { - await axios.get('https://thetool.xinon.at/Dashboard/logout', { withCredentials: true }); + await axios.get('/Dashboard/logout'); } catch (error) { console.warn("Logout request might have failed, but logging out on client.", error); } finally { isAuthenticated.value = false; - loginForm.username = ''; - loginForm.password = ''; - loginForm.twoFactorCode = ''; - loginForm.state = 'credentials'; - loginForm.error = ''; + Object.assign(loginForm, { + username: '', password: '', twoFactorCode: '', remember: false, + error: '', state: 'credentials', isLoading: false + }); workorders.value = []; } }; @@ -229,13 +180,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ if (newTheme === 'system') localStorage.removeItem('theme'); else localStorage.setItem('theme', newTheme); applyTheme(); - if(isSettingsOpen.value) isSettingsOpen.value = false; - if(showThemePicker.value) showThemePicker.value = false; + showThemePicker.value = false; }; // --- COMPUTED & METHODS --- const fcpOptions = computed(() => { - if (!workorders.value || workorders.value.length === 0) return [{ value: 'all', text: 'Alle FCPs' }]; + if (!workorders.value.length) return [{ value: 'all', text: 'Alle FCPs' }]; const fcps = [...new Set(workorders.value.map(wo => wo.rimo_fcp_name).filter(Boolean))].sort(); return [{ value: 'all', text: 'Alle FCPs' }, ...fcps.map(fcp => ({ value: fcp, text: fcp }))]; }); @@ -279,22 +229,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ })); }); - const isChecklistComplete = computed(() => checklist.value.every(item => item.completed)); + const isChecklistComplete = computed(() => checklist.value.length > 0 && checklist.value.every(item => item.completed)); const translatedDocs = computed(() => { - if (!documentation.docs.length || !tenantConfig.value?.documentationTypes) { - return documentation.docs; - } + if (!documentation.docs.length || !tenantConfig.value?.documentationTypes) return documentation.docs; const typeMap = new Map(tenantConfig.value.documentationTypes.map(t => [t.value, t.text])); - return documentation.docs.map(doc => ({ - ...doc, - translatedName: typeMap.get(doc.documentType) || doc.documentType, - })); + return documentation.docs.map(doc => ({ ...doc, translatedName: typeMap.get(doc.documentType) || doc.documentType })); }); - const filteredJournals = computed(() => { - return documentation.journals.filter(j => !j.text.toLowerCase().includes('wurde zugewiesen.')); - }); + const filteredJournals = computed(() => documentation.journals.filter(j => !j.text.toLowerCase().includes('wurde zugewiesen.'))); const fetchWorkorders = async () => { isLoading.value = true; @@ -302,8 +245,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ const response = await api.post(`/get`, { pagination: { page: 1, per_page: 500 } }); workorders.value = response.data.rows; } catch (error) { - console.error("Failed to fetch workorders:", error); - if (error.response && (error.response.status === 401 || error.response.status === 403)) { + if (error.response && [401, 403].includes(error.response.status)) { isAuthenticated.value = false; } } @@ -311,21 +253,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ }; const getStatusInfo = (status) => { - const statuses = { + return { 'new': { text: 'Neu', color: 'bg-blue-500' }, 'assigned': { text: 'Zugewiesen', color: 'bg-sky-500' }, 'scheduled': { text: 'Geplant', color: 'bg-amber-500' }, 'correction_requested': { text: 'Korrektur', color: 'bg-red-500' }, 'intervention_required': { text: 'Eingriff', color: 'bg-red-700' }, 'civil_engineering_required': { text: 'Tiefbau', color: 'bg-orange-500' }, 'civil_engineering_completed': { text: 'Tiefbau OK', color: 'bg-green-500' }, 'problem_solved': { text: 'Problem gelöst', color: 'bg-teal-500' }, 'documented': { text: 'Dokumentiert', color: 'bg-indigo-500' }, 'completed': { text: 'Abgeschlossen', color: 'bg-slate-500' }, - 'cancelled': { text: 'Storniert', color: 'bg-gray-600' }, 'default': { text: 'Unbekannt', color: 'bg-gray-400' } - }; - return statuses[status] || statuses.default; + 'cancelled': { text: 'Storniert', color: 'bg-gray-600' } + }[status] || { text: 'Unbekannt', color: 'bg-gray-400' }; }; - const formatDate = (timestamp, format = 'DD.MM.YYYY') => { - if (!timestamp) return '–'; - return moment.unix(timestamp).format(format); - }; + const formatDate = (timestamp, format = 'DD.MM.YYYY') => timestamp ? moment.unix(timestamp).format(format) : '–'; const fetchDetails = async (workorderId) => { isDetailsLoading.value = true; @@ -351,7 +289,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ isDetailsPanelOpen.value = false; setTimeout(() => { selectedWorkorder.value = null; - documentation.docs = []; documentation.journals = []; + Object.assign(documentation, { docs: [], journals: [] }); tenantConfig.value = null; isEditingInfo.value = false; }, 350); }; @@ -362,12 +300,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ }; const saveAdditionalInfo = async () => { - const newInfo = tempAdditionalInfo.value; try { - await api.post('/updateAdditionalInfo', { workorderId: selectedWorkorder.value.id, additionalInfo: newInfo }); - selectedWorkorder.value.additionalInfo = newInfo; + await api.post('/updateAdditionalInfo', { workorderId: selectedWorkorder.value.id, additionalInfo: tempAdditionalInfo.value }); + selectedWorkorder.value.additionalInfo = tempAdditionalInfo.value; const woInList = workorders.value.find(w => w.id === selectedWorkorder.value.id); - if(woInList) woInList.additionalInfo = newInfo; + if(woInList) woInList.additionalInfo = tempAdditionalInfo.value; await fetchDetails(selectedWorkorder.value.id); } catch(e) { console.error("Failed to save info", e); } finally { isEditingInfo.value = false; } @@ -380,8 +317,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ documentation.journals = response.data.journals; newJournalEntry.value = ''; await nextTick(() => { - const journalContainer = document.querySelector('.journal-container'); - if(journalContainer) journalContainer.scrollTop = journalContainer.scrollHeight; + const el = document.querySelector('.journal-container'); + if(el) el.scrollTop = el.scrollHeight; }); } catch(e) { console.error("Failed to add journal entry", e); } }; @@ -399,7 +336,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ const formData = new FormData(); formData.append('workorderId', selectedWorkorder.value.id); formData.append('documentType', uploadModal.documentType); - for (let i = 0; i < uploadModal.files.length; i++) formData.append('files[]', uploadModal.files[i]); + for (const file of uploadModal.files) formData.append('files[]', file); try { await api.post(`/uploadDocumentation`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }); await fetchDetails(selectedWorkorder.value.id); @@ -413,27 +350,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ const submitProblem = async () => { if (problemModal.selectedInterventions.length === 0) return; - let journalParts = []; - const sortedInterventions = [...problemModal.selectedInterventions].sort((a, b) => a.value.localeCompare(b.value)); - for (const type of sortedInterventions) { - let text = type.text; - const needsDetail = type.text.includes('X') || type.text.toLowerCase().includes('sonstiges'); - if (needsDetail) { - const detail = problemModal.details[type.value] || ''; - if (!detail) { - alert(`Bitte geben Sie Details für "${type.text}" an.`); - return; - } - text = text.includes('X') ? text.replace('X', detail) : `${text}: ${detail}`; - } - journalParts.push(text); - } try { + const journalParts = problemModal.selectedInterventions.map(type => { + let text = type.text; + const needsDetail = type.text.includes('X') || type.text.toLowerCase().includes('sonstiges'); + if (needsDetail) { + const detail = problemModal.details[type.value] || ''; + if (!detail) { + throw new Error(`Bitte geben Sie Details für "${type.text}" an.`); + } + text = text.includes('X') ? text.replace('X', detail) : `${text}: ${detail}`; + } + return text; + }); await api.post('/requestIntervention', { workorderId: selectedWorkorder.value.id, journalText: journalParts.join('\n') }); await fetchWorkorders(); closeDetails(); - } catch(e) { console.error("Failed to report problem", e); } - finally { problemModal.show = false; problemModal.selectedInterventions = []; problemModal.details = {}; } + } catch(e) { + alert(e.message || "Problem konnte nicht gemeldet werden."); + } finally { + problemModal.show = false; + problemModal.selectedInterventions = []; + problemModal.details = {}; + } }; const handleCompleteClick = () => { @@ -462,9 +401,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ // --- LIFECYCLE & WATCHERS --- onMounted(() => { isStandalone.value = window.matchMedia('(display-mode: standalone)').matches; - if (!localStorage.getItem('theme')) { - showThemePicker.value = true; - } + if (!localStorage.getItem('theme')) showThemePicker.value = true; theme.value = localStorage.getItem('theme') || 'system'; applyTheme(); window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', applyTheme); @@ -488,52 +425,75 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['action']) && $_POST[ }, template: ` -
- +
+ + + +

Authentifizierung wird geprüft...

-
+
- Logo -
+ Logo +

Anmelden

Bitte loggen Sie sich ein.

-