fix issues
Some checks are pending
build / test (macOS-latest) (push) Waiting to run
build / test (ubuntu-latest) (push) Waiting to run
build / test (windows-latest) (push) Waiting to run
build / build-release (push) Blocked by required conditions
build / notify (push) Blocked by required conditions
lint / go-lint (push) Waiting to run
lint / eslint (push) Waiting to run
lint / notify (push) Blocked by required conditions

This commit is contained in:
Ildar Kamalov 2026-07-09 12:06:44 +03:00
parent d20a5e297a
commit 86456f38d9
10 changed files with 52 additions and 18 deletions

View file

@ -47,12 +47,12 @@
border: none;
border-radius: 16px;
background-clip: border-box;
padding: 8px 0;
padding: 16px 0;
color: var(--default-main-text);
text-align: left;
overflow: auto;
@media (min-width: 1024px) {
@media (min-width: 768px) {
border-radius: 16px;
padding: 24px 16px;
}

View file

@ -34,6 +34,22 @@ export const Encryption = () => {
const [tlsStatusLoaded, setTlsStatusLoaded] = createSignal(false);
/**
* Shadows encryptionState.enabled with {@code equals: false} so we can
* force a DOM re-sync even when the value is unchanged (e.g. reverting
* after a modal opens without saving). Synced from store only when
* {@code processingConfig} is false to avoid flashing during async save.
*/
const [encryptionEnabled, setEncryptionEnabled] = createSignal(false, {
equals: false,
});
createEffect(() => {
if (!encryptionState.processingConfig) {
setEncryptionEnabled(encryptionState.enabled);
}
});
const [validateConfig, cancelValidation] = createDebouncedValidator();
onMount(async () => {
@ -49,9 +65,9 @@ export const Encryption = () => {
!!(encryptionState.certificate_chain || encryptionState.certificate_path);
const handleEncryptedDnsChange = (checked: boolean) => {
resetValidationStatus();
if (!checked) {
setEncryptionEnabled(false);
resetValidationStatus();
setTlsConfig(
{
enabled: false,
@ -72,6 +88,8 @@ export const Encryption = () => {
const hasServerName = !!encryptionState.server_name;
// Everything is set up — save the change.
// Native input already shows ON from the click; sync effect
// confirms on success or reverts on failure.
if (hasCert && hasKey && hasServerName) {
setTlsConfig(
{
@ -82,6 +100,10 @@ export const Encryption = () => {
return;
}
// Not saving — force DOM back to unchecked so the switch
// doesn't appear ON while encryption is actually OFF.
setEncryptionEnabled(false);
// Certificate or key is missing — open the TLS cert wizard (don't save yet).
if (!hasCert || !hasKey) {
setAddCertOpen(true);
@ -174,7 +196,7 @@ export const Encryption = () => {
</div>
<Show
when={tlsStatusLoaded() || !encryptionState.processing}
when={tlsStatusLoaded()}
fallback={<PageLoader />}
>
<PlainDnsToggle />
@ -195,7 +217,7 @@ export const Encryption = () => {
variant="switch"
title={intl.getMessage('encryption_encrypted_dns')}
description={intl.getMessage('encryption_encrypted_dns_desc')}
checked={encryptionState.enabled}
checked={encryptionEnabled()}
disabled={encryptionState.processingConfig}
onChange={handleEncryptedDnsChange}
/>

View file

@ -6,7 +6,9 @@ import { encryptionState, setTlsConfig } from 'panel/stores/encryption';
export const PlainDnsToggle = () => {
const [confirmDisable, setConfirmDisable] = createSignal(false);
const [localChecked, setLocalChecked] = createSignal(encryptionState.serve_plain_dns);
const [localChecked, setLocalChecked] = createSignal(encryptionState.serve_plain_dns, {
equals: false,
});
const enc = () => encryptionState;
// Sync localChecked from store when the dialog is not open,
@ -31,8 +33,9 @@ export const PlainDnsToggle = () => {
setTlsConfig({ serve_plain_dns: true });
return;
}
// Don't update localChecked — the switch stays visually on
// until the user confirms in the modal.
// Force DOM re-sync before opening the modal. With equals:false
// this re-applies checked={true} to the native input.
setLocalChecked((v) => v);
setConfirmDisable(true);
};

View file

@ -1,7 +1,7 @@
import { Show } from 'solid-js';
import { Icon } from 'panel/common/ui/Icon';
import intl from 'panel/common/intl';
import { encryptionState, setTlsConfig, resetValidationStatus } from 'panel/stores/encryption';
import { encryptionState, setTlsConfig, resetValidationStatus, clearCertOptimistically } from 'panel/stores/encryption';
import { CertificateStatus, KeyStatus, ValidationStatus } from '../Status';
import s from '../styles.module.pcss';
import theme from 'panel/lib/theme';
@ -10,6 +10,8 @@ export const TlsCertSection = () => {
const enc = () => encryptionState;
const removeCert = () => {
clearCertOptimistically();
resetValidationStatus();
setTlsConfig({
enabled: false,
serve_plain_dns: true,
@ -19,7 +21,6 @@ export const TlsCertSection = () => {
private_key_path: '',
private_key_saved: false,
});
resetValidationStatus();
};
const renderStatus = () => {

View file

@ -114,6 +114,7 @@ export const Auth = (props: Props) => {
autocomplete="username"
value={(field.value as string) ?? ''}
errorMessage={field.error as string}
size="large"
/>
)}
</Field>
@ -146,6 +147,7 @@ export const Auth = (props: Props) => {
})
}
errorMessage={field.error as string}
size="large"
/>
)}
</Field>
@ -175,6 +177,7 @@ export const Auth = (props: Props) => {
})
}
errorMessage={field.error as string}
size="large"
/>
)}
</Field>

View file

@ -86,6 +86,7 @@ export const DnsBanner = (props: Props) => {
const { value } = e.target as HTMLInputElement;
props.setDnsPort(toNumber(value));
}}
size="large"
/>
</div>

View file

@ -47,7 +47,9 @@ export const WebBanner = (props: Props) => {
<h3 class={styles.bannerTitle}>{intl.getMessage('setup_ui_title_banner')}</h3>
<div class={styles.bannerInputs}>
<div class={styles.form}>
<label class={styles.bannerLabel}>{intl.getMessage('network_interface')}</label>
<label for="install_web_ip" class={styles.bannerLabel}>
{intl.getMessage('network_interface')}
</label>
<Select
options={props.webIpOptions}
value={props.webIpOptions.find((option) => option.value === props.webIp())}
@ -60,7 +62,7 @@ export const WebBanner = (props: Props) => {
</div>
<div class={styles.form}>
<label class={styles.bannerLabel}>
<label for="install_web_port" class={styles.bannerLabel}>
{intl.getMessage('install_settings_port')}
</label>
<Input
@ -73,6 +75,7 @@ export const WebBanner = (props: Props) => {
const { value } = e.target as HTMLInputElement;
props.setWebPort(toNumber(value));
}}
size="large"
/>
</div>

View file

@ -17,7 +17,7 @@
}
.bannerLabel {
color: var(--gray-70);
color: var(--defaul-labels);
font-size: 14px;
font-style: normal;
font-weight: 400;
@ -51,7 +51,7 @@
}
.errorText {
color: var(--red-50);
color: var(--default-error-text);
}
.mutedText {
@ -65,5 +65,5 @@
.bannerList {
list-style: none;
padding: 0;
margin: 0
margin: 0;
}

View file

@ -16,7 +16,7 @@
.bannerList {
list-style: none;
padding: 0;
margin: 0
margin: 0;
}
.bannerItem {
@ -28,5 +28,5 @@
font-style: normal;
font-weight: 400;
line-height: 130%;
margin-top: 16px;
padding: 8px 0;
}

View file

@ -242,6 +242,7 @@ textarea {
align-items: flex-start;
border-radius: 16px;
box-shadow: 8px 8px 24px 0 rgba(0, 0, 0, 0.1);
background-color: var(--default-cards-background);
margin-bottom: 0;
}
}