Mobile responsiveness + IP allow list comments

Mobile improvements:
- Fix overflow issues with global max-width/overflow-x hidden
- Stack ticket detail selects on mobile instead of one long row
- Compact header: hide less important buttons, scrollable right side
- Stat cards in 2-column grid on mobile
- Force flex-wrap on inline-styled flex containers
- Hide subtitle and user-info on small phones
- Ticket selects full-width on small phones

IP allow list:
- Support # comments after IP addresses (e.g. "192.168.1.1 # Office VPN")
- Updated placeholder with comment examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 18:49:07 +02:00
parent 77dc790b0f
commit bbfff2f8b5
3 changed files with 160 additions and 3 deletions

View File

@@ -162,7 +162,14 @@ function getClientIp(): string {
function isIpAllowed(string $ip, string $allowedIps): bool {
$allowedIps = trim($allowedIps);
if ($allowedIps === '' || strtolower($allowedIps) === 'kaikki') return true;
$entries = preg_split('/[\s,]+/', $allowedIps, -1, PREG_SPLIT_NO_EMPTY);
// Tukee kommentteja: "192.168.1.1 # Yritys VPN" — risuaidan jälkeinen osa ohitetaan
$lines = preg_split('/\r?\n/', $allowedIps);
$entries = [];
foreach ($lines as $line) {
$line = preg_replace('/#.*$/', '', $line); // Poista kommentti
$parts = preg_split('/[\s,]+/', trim($line), -1, PREG_SPLIT_NO_EMPTY);
foreach ($parts as $p) $entries[] = $p;
}
// Normalisoi IP: IPv4-mapped IPv6 (::ffff:1.2.3.4) → myös IPv4 muotoon
$ipBin = @inet_pton($ip);

View File

@@ -1726,8 +1726,8 @@
</div>
<div id="company-allowed-ips-section" class="form-group" style="margin-top:1rem;display:none;">
<label style="font-weight:600;font-size:0.9rem;">Sallitut IP-osoitteet</label>
<textarea id="company-edit-allowed-ips" rows="3" style="font-family:monospace;font-size:0.85rem;" placeholder="192.168.1.100&#10;10.0.0.0/8"></textarea>
<small style="color:#888;">Yksi IP tai CIDR per rivi. "kaikki" = ei rajoitusta.</small>
<textarea id="company-edit-allowed-ips" rows="4" style="font-family:monospace;font-size:0.85rem;" placeholder="192.168.1.100 # Toimisto&#10;10.0.0.0/8 # Yritys VPN&#10;85.76.123.45 # Jukan koti"></textarea>
<small style="color:#888;">Yksi IP tai CIDR per rivi. Kommentti #-merkin jälkeen. "kaikki" = ei rajoitusta.</small>
</div>
<button class="btn-primary" id="btn-save-company-settings" style="font-size:0.85rem;">Tallenna asetukset</button>
</div>

150
style.css
View File

@@ -1223,6 +1223,156 @@ span.empty {
html {
overscroll-behavior: none;
}
/* === GLOBAALI OVERFLOW-ESTO === */
body, #app, .main-container, .tab-content {
max-width: 100vw;
overflow-x: hidden;
}
/* === HEADER: hamburger-tyylinen kompakti === */
header {
flex-wrap: nowrap;
}
.header-right {
flex-wrap: nowrap;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
max-width: 60vw;
}
.header-right::-webkit-scrollbar { display: none; }
/* Piilota "Käyttäjät" ja "Yritykset" napit mobiilissa — käytettävissä valikosta */
#btn-users, #btn-companies {
display: none !important;
}
.company-selector {
max-width: 120px;
font-size: 0.75rem !important;
}
/* === TIKETTI DETAIL: selectit stackiin === */
#ticket-detail-header > div:first-child > div:last-child,
#ticket-detail-header div[style*="display:flex"][style*="gap:0.5rem"] {
flex-wrap: wrap !important;
}
#ticket-detail-header select,
#ticket-detail-header button {
flex: 1 1 auto;
min-width: 0;
max-width: 48%;
}
/* === TAULUKOT: piilota vähemmän tärkeitä kolumneja === */
table {
font-size: 0.78rem;
}
/* === CHAT/VIESTIT: kompaktimpi === */
.ticket-message {
padding: 0.6rem !important;
margin-bottom: 0.5rem !important;
}
.ticket-message .msg-header {
flex-direction: column;
gap: 0.15rem;
}
/* === INLINE-TYYLIT YLIAJO: estä kiinteät leveydet === */
[style*="min-width:"] {
min-width: 0 !important;
}
[style*="width:300px"],
[style*="width:350px"],
[style*="width:400px"],
[style*="width:500px"],
[style*="width:600px"] {
width: 100% !important;
max-width: 100% !important;
}
/* === TAGIT & BADGET: wrappaa === */
[style*="display:flex"][style*="gap"] {
flex-wrap: wrap !important;
}
/* === TOOLBAR: kompaktimpi === */
.toolbar {
gap: 0.4rem !important;
}
.toolbar select,
.toolbar input,
.toolbar button {
font-size: 0.78rem;
}
/* === STAT CARDS: 2 per rivi === */
.sidebar-stats {
display: grid !important;
grid-template-columns: 1fr 1fr;
gap: 0.4rem;
}
.sidebar-stats .stat-card {
min-width: 0;
}
/* === LOMAKKEET: koko leveys === */
input, select, textarea {
max-width: 100%;
box-sizing: border-box;
}
/* === ASIAKKAAN DETAIL: stackattu === */
.detail-grid {
grid-template-columns: 1fr !important;
}
/* === MODAALIT: enemmän scrollvaraa === */
.modal-content {
max-height: 90vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
}
/* Pieni puhelin - lisäoptimointeja */
@media (max-width: 480px) {
/* Tab-bar: ikoni-tyylinen */
.tab {
padding: 0.5rem 0.55rem;
font-size: 0.68rem;
}
/* Header: erittäin kompakti */
.header-brand img {
height: 22px;
}
.brand-icon {
font-size: 1.2rem;
}
.subtitle {
display: none;
}
/* Piilota käyttäjäinfo */
.user-info {
display: none;
}
/* Tiketti selectit: yksi per rivi */
#ticket-detail-header select,
#ticket-detail-header button {
max-width: 100%;
width: 100%;
}
}
/* Tab bar */