fix(auth): restringe workshop/mecanico solo por servidor y cookie
- Elimina el script inline que redirigia a todos los usuarios. - auth_bp.py: la respuesta de login ahora setea cookie pos_role. - app.py: before_request redirige a /pos/workshop solo cuando la cookie indica rol workshop/mechanic (no depende de JS cacheado). - login.js/login.min.js/app-init.js: sincronizan/limpian la cookie pos_role junto con el token en login y logout. Tests: 35 passed
This commit is contained in:
14
pos/app.py
14
pos/app.py
@@ -1,4 +1,4 @@
|
||||
from flask import Flask
|
||||
from flask import Flask, request, redirect
|
||||
from json_provider import OrjsonProvider
|
||||
|
||||
|
||||
@@ -10,6 +10,18 @@ def create_app():
|
||||
from middleware_tenant import resolve_tenant
|
||||
app.before_request(resolve_tenant)
|
||||
|
||||
# ─── Server-side guard: workshop/mechanic users only see /pos/workshop ──────
|
||||
@app.before_request
|
||||
def restrict_workshop_users():
|
||||
path = request.path
|
||||
if not path.startswith('/pos/'):
|
||||
return
|
||||
if path == '/pos/workshop' or path.startswith('/pos/static/') or path.startswith('/pos/api/') or path == '/pos/sw.js' or path == '/pos/login' or path.startswith('/pos/login'):
|
||||
return
|
||||
role = request.cookies.get('pos_role', '').lower()
|
||||
if role in ('workshop', 'mechanic'):
|
||||
return redirect('/pos/workshop')
|
||||
|
||||
# ─── PWA: Service Worker must be served from /pos/ scope ──────
|
||||
@app.route('/pos/sw.js')
|
||||
def pos_sw():
|
||||
|
||||
@@ -5,7 +5,7 @@ import jwt
|
||||
import bcrypt
|
||||
import time
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from flask import Blueprint, request, jsonify, g
|
||||
from flask import Blueprint, request, jsonify, g, make_response
|
||||
from config import JWT_SECRET, JWT_ACCESS_EXPIRES, PIN_MAX_ATTEMPTS_PER_MINUTE, PIN_LOCKOUT_THRESHOLD, PIN_LOCKOUT_MINUTES
|
||||
from tenant_db import get_tenant_conn, get_master_conn
|
||||
|
||||
@@ -157,11 +157,17 @@ def login_pin():
|
||||
}
|
||||
token = jwt.encode(payload, JWT_SECRET, algorithm='HS256')
|
||||
|
||||
return jsonify({
|
||||
response = make_response(jsonify({
|
||||
'token': token,
|
||||
'employee': matched_employee,
|
||||
'permissions': permissions
|
||||
})
|
||||
}))
|
||||
# Cookie used by server-side route guards; JS can also read it for quick checks.
|
||||
response.set_cookie(
|
||||
'pos_role', matched_employee['role'],
|
||||
path='/pos', samesite='Lax', httponly=False
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
@auth_bp.route('/employees/<int:tenant_id>', methods=['GET'])
|
||||
|
||||
@@ -118,6 +118,7 @@
|
||||
localStorage.removeItem('pos_employee');
|
||||
localStorage.removeItem('pos_tenant_id');
|
||||
localStorage.removeItem('pos_cart');
|
||||
document.cookie = 'pos_role=; path=/pos; SameSite=Lax; expires=Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
window.location.href = '/pos/login';
|
||||
};
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
localStorage.setItem('pos_token', result.data.token);
|
||||
localStorage.setItem('pos_employee', JSON.stringify(result.data.employee));
|
||||
localStorage.setItem('pos_tenant_id', tenantId);
|
||||
document.cookie = 'pos_role=' + (result.data.employee.role || '') + '; path=/pos; SameSite=Lax';
|
||||
window.location.href = '/pos/catalog';
|
||||
})
|
||||
.catch(function() {
|
||||
|
||||
2
pos/static/js/login.min.js
vendored
2
pos/static/js/login.min.js
vendored
@@ -1 +1 @@
|
||||
!function(){"use strict";var t="",e=document.querySelectorAll("#pinDots .pin-dot"),n=document.getElementById("loginError"),o=new URLSearchParams(window.location.search).get("tenant")||localStorage.getItem("pos_tenant_id"),a=localStorage.getItem("pos_device_id");function i(){e.forEach((function(e,n){e.classList.toggle("filled",n<t.length)}))}a||(a="dev-"+Date.now()+"-"+Math.random().toString(36).substr(2,9),localStorage.setItem("pos_device_id",a)),window.addDigit=function(e){t.length>=4||(t+=e,i(),n.textContent="",4===t.length&&submitPin())},window.clearPin=function(){t="",i(),n.textContent=""},window.submitPin=function(){4===t.length&&(n.textContent="",fetch("/pos/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenant_id:parseInt(o),pin:t,device_id:a})}).then((function(t){return t.json().then((function(e){return{ok:t.ok,data:e}}))})).then((function(t){if(!t.ok)return n.textContent=t.data.error||"Error de autenticacion",void clearPin();localStorage.setItem("pos_token",t.data.token),localStorage.setItem("pos_employee",JSON.stringify(t.data.employee)),localStorage.setItem("pos_tenant_id",o),window.location.href="/pos/catalog"})).catch((function(){n.textContent="Error de conexion",clearPin()})))},document.addEventListener("keydown",(function(t){t.key>="0"&&t.key<="9"?addDigit(t.key):"Backspace"===t.key?clearPin():"Enter"===t.key&&submitPin()}));var r=localStorage.getItem("pos_token");r&&o&&(!function(t){try{var e=t.split(".");if(3!==e.length)return!1;var n=e[1].replace(/-/g,"+").replace(/_/g,"/"),o=JSON.parse(atob(n));return!!o.exp&&1e3*o.exp>Date.now()+3e4}catch(t){return!1}}(r)?(localStorage.removeItem("pos_token"),localStorage.removeItem("pos_employee")):window.location.href="/pos/catalog")}();
|
||||
!function(){"use strict";var t="",e=document.querySelectorAll("#pinDots .pin-dot"),n=document.getElementById("loginError"),o=new URLSearchParams(window.location.search).get("tenant")||localStorage.getItem("pos_tenant_id"),a=localStorage.getItem("pos_device_id");function i(){e.forEach((function(e,n){e.classList.toggle("filled",n<t.length)}))}a||(a="dev-"+Date.now()+"-"+Math.random().toString(36).substr(2,9),localStorage.setItem("pos_device_id",a)),window.addDigit=function(e){t.length>=4||(t+=e,i(),n.textContent="",4===t.length&&submitPin())},window.clearPin=function(){t="",i(),n.textContent=""},window.submitPin=function(){4===t.length&&(n.textContent="",fetch("/pos/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenant_id:parseInt(o),pin:t,device_id:a})}).then((function(t){return t.json().then((function(e){return{ok:t.ok,data:e}}))})).then((function(t){if(!t.ok)return n.textContent=t.data.error||"Error de autenticacion",void clearPin();localStorage.setItem("pos_token",t.data.token),localStorage.setItem("pos_employee",JSON.stringify(t.data.employee)),localStorage.setItem("pos_tenant_id",o),document.cookie="pos_role="+(t.data.employee.role||"")+"; path=/pos; SameSite=Lax",window.location.href="/pos/catalog"})).catch((function(){n.textContent="Error de conexion",clearPin()})))},document.addEventListener("keydown",(function(t){t.key>="0"&&t.key<="9"?addDigit(t.key):"Backspace"===t.key?clearPin():"Enter"===t.key&&submitPin()}));var r=localStorage.getItem("pos_token");r&&o&&(!function(t){try{var e=t.split(".");if(3!==e.length)return!1;var n=e[1].replace(/-/g,"+").replace(/_/g,"/"),o=JSON.parse(atob(n));return!!o.exp&&1e3*o.exp>Date.now()+3e4}catch(t){return!1}}(r)?(localStorage.removeItem("pos_token"),localStorage.removeItem("pos_employee")):window.location.href="/pos/catalog")}();
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/accounting.css?v=33">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/accounting.css?v=33"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -17,20 +17,7 @@
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
<script src="/pos/static/js/native-bridge.js" defer></script>
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/catalog.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/catalog.css"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/config.css?v=33">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/config.css?v=33"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/customers.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/customers.css"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/dashboard.css?v=33">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/dashboard.css?v=33"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -17,20 +17,7 @@
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
<script src="/pos/static/js/native-bridge.js" defer></script>
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/diagrams.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/diagrams.css"></head>
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/fleet.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/fleet.css"></head>
|
||||
<body>
|
||||
|
||||
<div class="page-shell">
|
||||
|
||||
@@ -34,20 +34,7 @@
|
||||
.pagination button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.pagination span { font-size: 13px; color: #4b5563; }
|
||||
.loading { text-align: center; padding: 40px; color: #6b7280; }
|
||||
</style>
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
</style></head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>📊 Ventas Históricas - Atlas</h1>
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/inventory.css?v=33">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/inventory.css?v=33"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/invoicing.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/invoicing.css"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/marketplace.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/marketplace.css"></head>
|
||||
<body>
|
||||
|
||||
<header class="page-header">
|
||||
|
||||
@@ -28,20 +28,7 @@
|
||||
.meli-config-row { display:flex;gap:var(--space-4);flex-wrap:wrap;margin-bottom:var(--space-4); }
|
||||
.meli-config-row label { display:block;font-size:var(--text-caption);color:var(--color-text-muted);margin-bottom:4px; }
|
||||
.meli-config-row input, .meli-config-row select { padding:8px 12px;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface-0);color:var(--color-text-primary); }
|
||||
</style>
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
</style></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -16,20 +16,7 @@
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
<script src="/pos/static/js/native-bridge.js" defer></script>
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/pos.css?v=33">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/pos.css?v=33"></head>
|
||||
|
||||
<body class="pos-shell" id="appBody">
|
||||
|
||||
|
||||
@@ -11,20 +11,7 @@
|
||||
<link rel="stylesheet" href="/pos/static/css/pos-ui.css?v=33" />
|
||||
<link rel="stylesheet" href="/pos/static/css/sidebar.css" />
|
||||
<link rel="stylesheet" href="/pos/static/css/pos-glass.css" />
|
||||
<link rel="stylesheet" href="/pos/static/css/quotations.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/quotations.css"></head>
|
||||
<body>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/i18n.js" defer></script>
|
||||
|
||||
@@ -15,20 +15,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/reports.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/reports.css"></head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
@@ -49,20 +49,7 @@
|
||||
.sc-interchange-list { display:flex; flex-wrap:wrap; gap:var(--space-2); }
|
||||
.sc-interchange-chip { background:var(--color-surface-2); border:1px solid var(--color-border); border-radius:var(--radius-full); padding:2px 10px; font-size:var(--text-caption); }
|
||||
.sc-close { background:none; border:none; font-size:20px; color:var(--color-text-muted); cursor:pointer; }
|
||||
</style>
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
</style></head>
|
||||
<body>
|
||||
|
||||
<!-- Theme bar -->
|
||||
|
||||
@@ -18,20 +18,7 @@
|
||||
<meta name="theme-color" content="#F5A623" />
|
||||
<link rel="shortcut icon" type="image/png" href="/pos/static/pwa/icon-192.png" />
|
||||
|
||||
<link rel="stylesheet" href="/pos/static/css/whatsapp.css">
|
||||
<script id="workshop-mechanic-redirect">
|
||||
(function(){
|
||||
var t=localStorage.getItem("pos_token");
|
||||
if(!t)return;
|
||||
try{var p=JSON.parse(atob(t.split(".")[1]));var r=(p.role||"").toLowerCase();
|
||||
if((r==="workshop"||r==="mechanic")&&window.location.pathname!=="/pos/workshop"){
|
||||
if("serviceWorker" in navigator){navigator.serviceWorker.getRegistrations().then(function(rs){rs.forEach(function(reg){reg.unregister();});});}
|
||||
window.location.replace("/pos/workshop");
|
||||
}
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/pos/static/css/whatsapp.css"></head>
|
||||
<body>
|
||||
|
||||
<div class="page-shell">
|
||||
|
||||
Reference in New Issue
Block a user