mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-17 13:52:14 +01:00
fix(frontend): prevent hydration errors in admin security form and improve autofill support (#5613)
# Description of Changes ## What was changed - Updated several Mantine `label` compositions in `AdminSecuritySection.tsx` to avoid invalid HTML nesting that can trigger React hydration errors (e.g., a `<div>` rendered inside a `<p>`). - Changed `Group` used inside `NumberInput` / `Select` labels to render as an inline element via `component="span"`. - Added `name` attributes to multiple form controls (`Switch`, `Select`, `NumberInput`, `Textarea`) to satisfy browser/autofill recommendations and improve form field identification. ## Why the change was made - Fixes the runtime warning/error: - `In HTML, <div> cannot be a descendant of <p>. This will cause a hydration error.` - Caused by block-level wrappers inside Mantine `Text`/`p` label rendering. - Addresses the browser audit warning: - `A form field element should have an id or name attribute` - Adding stable `name` attributes improves autofill behavior and form accessibility tooling. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
@@ -219,6 +219,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="enableLogin"
|
||||
checked={settings?.enableLogin || false}
|
||||
onChange={(e) => setSettings({ ...settings, enableLogin: e.target.checked })}
|
||||
disabled={!loginEnabled}
|
||||
@@ -229,6 +230,7 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<Select
|
||||
name="loginMethod"
|
||||
label={t('admin.settings.security.loginMethod.label', 'Login Method')}
|
||||
description={t('admin.settings.security.loginMethod.description', 'The authentication method to use for user login')}
|
||||
value={settings?.loginMethod || 'all'}
|
||||
@@ -251,8 +253,9 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<NumberInput
|
||||
name="loginAttemptCount"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.loginAttemptCount.label', 'Login Attempt Limit')}</span>
|
||||
<PendingBadge show={isFieldPending('loginAttemptCount')} />
|
||||
</Group>
|
||||
@@ -268,8 +271,9 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<NumberInput
|
||||
name="loginResetTimeMinutes"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.loginResetTimeMinutes.label', 'Login Reset Time (minutes)')}</span>
|
||||
<PendingBadge show={isFieldPending('loginResetTimeMinutes')} />
|
||||
</Group>
|
||||
@@ -285,8 +289,9 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<Select
|
||||
name="xFrameOptions"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.xFrameOptions.label', 'X-Frame-Options')}</span>
|
||||
<PendingBadge show={isFieldPending('xFrameOptions')} />
|
||||
</Group>
|
||||
@@ -332,6 +337,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="jwt_persistence"
|
||||
checked={settings?.jwt?.persistence || false}
|
||||
onChange={(e) => setSettings({ ...settings, jwt: { ...settings?.jwt, persistence: e.target.checked } })}
|
||||
disabled={!loginEnabled}
|
||||
@@ -349,6 +355,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="jwt_enableKeyRotation"
|
||||
checked={settings?.jwt?.enableKeyRotation || false}
|
||||
onChange={(e) => setSettings({ ...settings, jwt: { ...settings?.jwt, enableKeyRotation: e.target.checked } })}
|
||||
disabled={!loginEnabled}
|
||||
@@ -366,6 +373,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="jwt_enableKeyCleanup"
|
||||
checked={settings?.jwt?.enableKeyCleanup || false}
|
||||
onChange={(e) => setSettings({ ...settings, jwt: { ...settings?.jwt, enableKeyCleanup: e.target.checked } })}
|
||||
disabled={!loginEnabled}
|
||||
@@ -376,8 +384,9 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<NumberInput
|
||||
name="jwt_keyRetentionDays"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.jwt.keyRetentionDays.label', 'Key Retention Days')}</span>
|
||||
<PendingBadge show={isFieldPending('jwt.keyRetentionDays')} />
|
||||
</Group>
|
||||
@@ -400,6 +409,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="jwt_secureCookie"
|
||||
checked={settings?.jwt?.secureCookie || false}
|
||||
onChange={(e) => setSettings({ ...settings, jwt: { ...settings?.jwt, secureCookie: e.target.checked } })}
|
||||
disabled={!loginEnabled}
|
||||
@@ -427,6 +437,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="audit_enabled"
|
||||
checked={settings?.audit?.enabled || false}
|
||||
onChange={(e) => setSettings({ ...settings, audit: { ...settings?.audit, enabled: e.target.checked } })}
|
||||
disabled={!loginEnabled}
|
||||
@@ -437,8 +448,9 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<NumberInput
|
||||
name="audit_level"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.audit.level.label', 'Audit Level')}</span>
|
||||
<PendingBadge show={isFieldPending('audit.level')} />
|
||||
</Group>
|
||||
@@ -454,8 +466,9 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<NumberInput
|
||||
name="audit_retentionDays"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.audit.retentionDays.label', 'Audit Retention (days)')}</span>
|
||||
<PendingBadge show={isFieldPending('audit.retentionDays')} />
|
||||
</Group>
|
||||
@@ -490,6 +503,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="html_urlSecurity_enabled"
|
||||
checked={settings?.html?.urlSecurity?.enabled || false}
|
||||
onChange={(e) => setSettings({
|
||||
...settings,
|
||||
@@ -506,8 +520,9 @@ export default function AdminSecuritySection() {
|
||||
|
||||
<div>
|
||||
<Select
|
||||
name="html_urlSecurity_level"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.htmlUrlSecurity.level.label', 'Security Level')}</span>
|
||||
<PendingBadge show={isFieldPending('html.urlSecurity.level')} />
|
||||
</Group>
|
||||
@@ -539,8 +554,9 @@ export default function AdminSecuritySection() {
|
||||
{/* Allowed Domains */}
|
||||
<div>
|
||||
<Textarea
|
||||
name="html_urlSecurity_allowedDomains"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.htmlUrlSecurity.allowedDomains.label', 'Allowed Domains (Whitelist)')}</span>
|
||||
<PendingBadge show={isFieldPending('html.urlSecurity.allowedDomains')} />
|
||||
</Group>
|
||||
@@ -567,8 +583,9 @@ export default function AdminSecuritySection() {
|
||||
{/* Blocked Domains */}
|
||||
<div>
|
||||
<Textarea
|
||||
name="html_urlSecurity_blockedDomains"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.htmlUrlSecurity.blockedDomains.label', 'Blocked Domains (Blacklist)')}</span>
|
||||
<PendingBadge show={isFieldPending('html.urlSecurity.blockedDomains')} />
|
||||
</Group>
|
||||
@@ -595,8 +612,9 @@ export default function AdminSecuritySection() {
|
||||
{/* Internal TLDs */}
|
||||
<div>
|
||||
<Textarea
|
||||
name="html_urlSecurity_internalTlds"
|
||||
label={
|
||||
<Group gap="xs">
|
||||
<Group component="span" gap="xs">
|
||||
<span>{t('admin.settings.security.htmlUrlSecurity.internalTlds.label', 'Internal TLDs')}</span>
|
||||
<PendingBadge show={isFieldPending('html.urlSecurity.internalTlds')} />
|
||||
</Group>
|
||||
@@ -632,6 +650,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="html_urlSecurity_blockPrivateNetworks"
|
||||
checked={settings?.html?.urlSecurity?.blockPrivateNetworks || false}
|
||||
onChange={(e) => setSettings({
|
||||
...settings,
|
||||
@@ -655,6 +674,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="html_urlSecurity_blockLocalhost"
|
||||
checked={settings?.html?.urlSecurity?.blockLocalhost || false}
|
||||
onChange={(e) => setSettings({
|
||||
...settings,
|
||||
@@ -678,6 +698,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="html_urlSecurity_blockLinkLocal"
|
||||
checked={settings?.html?.urlSecurity?.blockLinkLocal || false}
|
||||
onChange={(e) => setSettings({
|
||||
...settings,
|
||||
@@ -701,6 +722,7 @@ export default function AdminSecuritySection() {
|
||||
</div>
|
||||
<Group gap="xs">
|
||||
<Switch
|
||||
name="html_urlSecurity_blockCloudMetadata"
|
||||
checked={settings?.html?.urlSecurity?.blockCloudMetadata || false}
|
||||
onChange={(e) => setSettings({
|
||||
...settings,
|
||||
|
||||
Reference in New Issue
Block a user