fix: improve mobile responsiveness for admin settings (#3431)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Ben Weeks 2025-10-21 11:54:49 +01:00 committed by GitHub
parent 55c5ab3a6d
commit 785fb7af8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 221 additions and 114 deletions

View file

@ -4,7 +4,7 @@
</h6> </h6>
<div class="row"> <div class="row">
<div class="col-md-8 col-sm-12"> <div class="col-12 col-md-8">
<div class="q-pa-sm"> <div class="q-pa-sm">
<canvas <canvas
style=" style="
@ -17,7 +17,7 @@
></canvas> ></canvas>
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-12"> <div class="col-12 col-md-4">
<q-input <q-input
filled filled
v-model="formData.lnbits_exchange_history_refresh_interval_seconds" v-model="formData.lnbits_exchange_history_refresh_interval_seconds"
@ -74,100 +74,104 @@
</div> </div>
</div> </div>
<q-table <div class="overflow-auto">
row-key="name" <q-table
:rows="formData.lnbits_exchange_rate_providers" row-key="name"
:columns="exchangesTable.columns" :rows="formData.lnbits_exchange_rate_providers"
v-model:pagination="exchangesTable.pagination" :columns="exchangesTable.columns"
> v-model:pagination="exchangesTable.pagination"
<template v-slot:header="props"> :grid="$q.screen.xs"
<q-tr :props="props"> :dense="$q.screen.lt.md"
<q-th auto-width></q-th> >
<q-th v-for="col in props.cols" :key="col.name" :props="props"> <template v-slot:header="props">
<span v-text="col.label"></span> <q-tr :props="props">
</q-th> <q-th auto-width></q-th>
</q-tr> <q-th v-for="col in props.cols" :key="col.name" :props="props">
</template> <span v-text="col.label"></span>
<template v-slot:body="props"> </q-th>
<q-tr :props="props"> </q-tr>
<q-td> </template>
<q-btn <template v-slot:body="props">
@click="removeExchangeProvider(props.row)" <q-tr :props="props">
round <q-td>
icon="delete" <q-btn
size="sm" @click="removeExchangeProvider(props.row)"
color="negative" round
class="q-ml-xs" icon="delete"
> size="sm"
</q-btn> color="negative"
</q-td> class="q-ml-xs"
<q-td> >
<q-input </q-btn>
dense </q-td>
filled <q-td>
v-model="props.row.name" <q-input
@update:model-value="touchSettings()" dense
type="text" filled
> v-model="props.row.name"
</q-input> @update:model-value="touchSettings()"
</q-td> type="text"
<q-td full-width> >
<q-input </q-input>
dense </q-td>
filled <q-td full-width>
v-model="props.row.api_url" <q-input
@update:model-value="touchSettings()" dense
type="text" filled
> v-model="props.row.api_url"
</q-input @update:model-value="touchSettings()"
></q-td> type="text"
<q-td> >
<q-input </q-input
dense ></q-td>
filled <q-td>
v-model="props.row.path" <q-input
@update:model-value="touchSettings()" dense
type="text" filled
> v-model="props.row.path"
</q-input> @update:model-value="touchSettings()"
</q-td> type="text"
<q-td> >
<q-select </q-input>
filled </q-td>
dense <q-td>
v-model="props.row.exclude_to" <q-select
@update:model-value="touchSettings()" filled
multiple dense
:options="{{ currencies | safe }}" v-model="props.row.exclude_to"
></q-select> @update:model-value="touchSettings()"
</q-td> multiple
<q-td> :options="{{ currencies | safe }}"
<q-btn ></q-select>
@click="showTickerConversionDialog(props.row)" </q-td>
round <q-td>
icon="add" <q-btn
size="sm" @click="showTickerConversionDialog(props.row)"
color="gray" round
class="q-ml-xs" icon="add"
> size="sm"
</q-btn> color="gray"
<q-chip class="q-ml-xs"
v-for="ticker, index in props.row.ticker_conversion" >
:key="ticker" </q-btn>
removable <q-chip
dense v-for="ticker, index in props.row.ticker_conversion"
filled :key="ticker"
@remove="removeExchangeTickerConversion(props.row, ticker)" removable
color="primary" dense
text-color="white" filled
:label="ticker" @remove="removeExchangeTickerConversion(props.row, ticker)"
class="ellipsis" color="primary"
> text-color="white"
</q-chip> :label="ticker"
</q-td> class="ellipsis"
</q-tr> >
</template> </q-chip>
</q-table> </q-td>
</q-tr>
</template>
</q-table>
</div>
<ul> <ul>
<li> <li>
<code>API URL</code> and <code>JSON Path</code> fields can use the <code>API URL</code> and <code>JSON Path</code> fields can use the

View file

@ -2,7 +2,7 @@
<q-card-section class="q-pa-none"> <q-card-section class="q-pa-none">
<h6 class="q-my-none"><span v-text="$t('server_management')"></span></h6> <h6 class="q-my-none"><span v-text="$t('server_management')"></span></h6>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-12 col-md-6">
<p><span v-text="$t('base_url')"></span></p> <p><span v-text="$t('base_url')"></span></p>
<q-input <q-input
filled filled
@ -16,7 +16,7 @@
<span v-text="$t('authentication')"></span> <span v-text="$t('authentication')"></span>
</h6> </h6>
<div class="row q-col-gutter-sm"> <div class="row q-col-gutter-sm">
<div class="col-12 col-sm-6"> <div class="col-12 col-md-6">
<q-input <q-input
filled filled
v-model="formData.auth_token_expire_minutes" v-model="formData.auth_token_expire_minutes"
@ -26,7 +26,7 @@
> >
</q-input> </q-input>
</div> </div>
<div class="col-12 col-sm-6"> <div class="col-12 col-md-6">
<q-select <q-select
filled filled
v-model="formData.auth_allowed_methods" v-model="formData.auth_allowed_methods"
@ -34,6 +34,7 @@
:hint="$t('auth_allowed_methods_hint')" :hint="$t('auth_allowed_methods_hint')"
:label="$t('auth_allowed_methods_label')" :label="$t('auth_allowed_methods_label')"
:options="formData.auth_all_methods" :options="formData.auth_all_methods"
:option-label="option => option.length > 25 ? option.substring(0, 22) + '...' : option"
></q-select> ></q-select>
</div> </div>
</div> </div>
@ -79,7 +80,7 @@
<strong class="q-my-none q-mb-sm">Google Auth</strong> <strong class="q-my-none q-mb-sm">Google Auth</strong>
<div class="row"> <div class="row">
<div class="col-md-6 col-sm-12 q-pr-sm"> <div class="col-12 col-md-6 q-pr-sm">
<q-input <q-input
filled filled
v-model="formData.google_client_id" v-model="formData.google_client_id"
@ -88,7 +89,7 @@
> >
</q-input> </q-input>
</div> </div>
<div class="col-md-6 col-sm-12"> <div class="col-12 col-md-6">
<q-input <q-input
filled filled
v-model="formData.google_client_secret" v-model="formData.google_client_secret"
@ -106,7 +107,7 @@
<strong class="q-my-none q-mb-sm">GitHub Auth</strong> <strong class="q-my-none q-mb-sm">GitHub Auth</strong>
<div class="row"> <div class="row">
<div class="col-md-6 col-sm-12 q-pr-sm"> <div class="col-12 col-md-6 q-pr-sm">
<q-input <q-input
filled filled
v-model="formData.github_client_id" v-model="formData.github_client_id"
@ -115,7 +116,7 @@
> >
</q-input> </q-input>
</div> </div>
<div class="col-md-6 col-sm-12"> <div class="col-12 col-md-6">
<q-input <q-input
filled filled
v-model="formData.github_client_secret" v-model="formData.github_client_secret"
@ -133,7 +134,7 @@
<strong class="q-my-none q-mb-sm">Keycloak Auth</strong> <strong class="q-my-none q-mb-sm">Keycloak Auth</strong>
<div class="row q-col-gutter-sm q-col-gutter-y-md"> <div class="row q-col-gutter-sm q-col-gutter-y-md">
<div class="col-md-4 col-sm-12"> <div class="col-12 col-md-4">
<q-input <q-input
filled filled
v-model="formData.keycloak_discovery_url" v-model="formData.keycloak_discovery_url"
@ -141,7 +142,7 @@
> >
</q-input> </q-input>
</div> </div>
<div class="col-md-4 col-sm-12"> <div class="col-12 col-md-4">
<q-input <q-input
filled filled
v-model="formData.keycloak_client_id" v-model="formData.keycloak_client_id"
@ -150,7 +151,7 @@
> >
</q-input> </q-input>
</div> </div>
<div class="col-md-4 col-sm-12"> <div class="col-12 col-md-4">
<q-input <q-input
filled filled
v-model="formData.keycloak_client_secret" v-model="formData.keycloak_client_secret"
@ -159,7 +160,7 @@
> >
</q-input> </q-input>
</div> </div>
<div class="col-md-4 col-sm-12"> <div class="col-12 col-md-4">
<q-input <q-input
filled filled
v-model="formData.keycloak_client_custom_org" v-model="formData.keycloak_client_custom_org"
@ -167,7 +168,7 @@
> >
</q-input> </q-input>
</div> </div>
<div class="col-md-8 col-sm-12"> <div class="col-12 col-md-8">
<q-input <q-input
filled filled
v-model="formData.keycloak_client_custom_icon" v-model="formData.keycloak_client_custom_icon"
@ -213,7 +214,7 @@
<div class="col-12 col-md-12"> <div class="col-12 col-md-12">
<p v-text="$t('ip_blocker')"></p> <p v-text="$t('ip_blocker')"></p>
<div class="row q-col-gutter-md"> <div class="row q-col-gutter-md">
<div class="col-6"> <div class="col-12 col-md-6">
<q-input <q-input
filled filled
v-model="formBlockedIPs" v-model="formBlockedIPs"
@ -243,7 +244,7 @@
</div> </div>
<br /> <br />
</div> </div>
<div class="col-6"> <div class="col-12 col-md-6">
<q-input <q-input
filled filled
v-model="formAllowedIPs" v-model="formAllowedIPs"
@ -279,7 +280,7 @@
<div class="col-12 col-md-12"> <div class="col-12 col-md-12">
<p v-text="$t('rate_limiter')"></p> <p v-text="$t('rate_limiter')"></p>
<div class="row q-col-gutter-md"> <div class="row q-col-gutter-md">
<div class="col-6"> <div class="col-12 col-md-6">
<q-input <q-input
filled filled
type="number" type="number"
@ -287,7 +288,7 @@
:label="$t('number_of_requests')" :label="$t('number_of_requests')"
></q-input> ></q-input>
</div> </div>
<div class="col-6"> <div class="col-12 col-md-6">
<q-select <q-select
filled filled
:options="[$t('second'),$t('minute'),$t('hour')]" :options="[$t('second'),$t('minute'),$t('hour')]"

View file

@ -76,7 +76,30 @@ import window_vars with context %} {% block scripts %} {{ window_vars(user) }}
<div class="row q-col-gutter-md justify-center"> <div class="row q-col-gutter-md justify-center">
<div class="col q-gutter-y-md"> <div class="col q-gutter-y-md">
<q-card> <q-card>
<q-splitter> <!-- Mobile: Horizontal tabs at top -->
<q-tabs
v-if="$q.screen.lt.md"
@update:model-value="showExchangeProvidersTab"
v-model="tab"
dense
active-color="primary"
inline-label
class="text-primary"
>
<q-tab name="funding" icon="account_balance_wallet" label="Fund" />
<q-tab name="security" icon="security" label="Sec" />
<q-tab name="server" icon="settings" label="Srv" />
<q-tab name="exchange_providers" icon="swap_horiz" label="Exch" />
<q-tab name="fiat_providers" icon="account_balance" label="Fiat" />
<q-tab name="extensions" icon="extension" label="Ext" />
<q-tab name="notifications" icon="notifications" label="Not" />
<q-tab name="audit" icon="receipt_long" label="Aud" />
<q-tab name="site_customisation" icon="language" label="Site" />
<q-tab name="library" icon="image" label="Lib" />
</q-tabs>
<!-- Desktop: Vertical sidebar with splitter -->
<q-splitter v-if="$q.screen.gt.sm">
<template v-slot:before> <template v-slot:before>
<q-tabs <q-tabs
@update:model-value="showExchangeProvidersTab" @update:model-value="showExchangeProvidersTab"
@ -182,7 +205,6 @@ import window_vars with context %} {% block scripts %} {{ window_vars(user) }}
<q-tab-panels <q-tab-panels
v-model="tab" v-model="tab"
animated animated
swipeable
vertical vertical
scroll scroll
transition-prev="jump-up" transition-prev="jump-up"
@ -202,6 +224,35 @@ import window_vars with context %} {% block scripts %} {{ window_vars(user) }}
</q-form> </q-form>
</template> </template>
</q-splitter> </q-splitter>
<!-- Mobile: Full-width content without sidebar -->
<q-form
v-if="$q.screen.lt.md"
name="settings_form"
id="settings_form_mobile"
>
<q-scroll-area style="height: 70vh">
<q-tab-panels
v-model="tab"
animated
vertical
scroll
transition-prev="jump-up"
transition-next="jump-up"
style="overflow-x: hidden; padding: 8px"
>
{% include "admin/_tab_funding.html" %} {% include
"admin/_tab_users.html" %} {% include "admin/_tab_server.html" %} {%
include "admin/_tab_exchange_providers.html" %}{% include
"admin/_tab_fiat_providers.html" %} {% include
"admin/_tab_extensions.html" %} {% include
"admin/_tab_notifications.html" %} {% include
"admin/_tab_security.html" %} {% include "admin/_tab_theme.html"
%}{% include "admin/_tab_audit.html"%}{% include
"admin/_tab_library.html"%}
</q-tab-panels>
</q-scroll-area>
</q-form>
</q-card> </q-card>
</div> </div>
</div> </div>

View file

@ -23,6 +23,57 @@
.wallet-list-card:first-child { .wallet-list-card:first-child {
margin-left: 1px; margin-left: 1px;
} }
/* Force chip content to wrap on mobile */
@media (max-width: 1023px) {
.q-chip {
height: auto !important;
min-height: 2rem !important;
}
.q-chip .q-chip__content {
white-space: normal !important;
word-break: break-all !important;
min-height: 1.5rem !important;
height: auto !important;
}
.q-chip .ellipsis {
overflow: visible !important;
text-overflow: clip !important;
white-space: normal !important;
word-break: break-all !important;
}
/* Fix q-input hint text wrapping and container growth */
.q-field {
overflow: visible !important;
height: auto !important;
min-height: 56px !important;
}
.q-field__inner {
overflow: visible !important;
height: auto !important;
}
.q-field__control {
overflow: visible !important;
height: auto !important;
}
.q-field__bottom {
position: relative !important;
transform: none !important;
max-height: none !important;
overflow: visible !important;
height: auto !important;
margin-bottom: 8px !important;
}
.q-field__messages {
min-height: auto !important;
}
.q-field__messages,
.q-field__messages > div {
white-space: normal !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
word-break: break-word !important;
}
}
</style> </style>
<title>{% block title %}{{ SITE_TITLE }}{% endblock %}</title> <title>{% block title %}{{ SITE_TITLE }}{% endblock %}</title>
<meta charset="utf-8" /> <meta charset="utf-8" />