2026-01-03 02:44:25 +00:00
import {
DeesElement ,
customElement ,
html ,
css ,
cssManager ,
property ,
type TemplateResult ,
} from '@design.estate/dees-element' ;
declare global {
interface HTMLElementTagNameMap {
'sz-settings-view' : SzSettingsView ;
}
}
export interface ISettings {
darkMode : boolean ;
cloudflareToken : string ;
cloudflareZoneId : string ;
autoRenewCerts : boolean ;
renewalThreshold : number ;
acmeEmail : string ;
httpPort : number ;
httpsPort : number ;
forceHttps : boolean ;
}
@customElement ( 'sz-settings-view' )
export class SzSettingsView extends DeesElement {
public static demo = ( ) = > html `
< div style = "padding: 24px; max-width: 800px;" >
< sz - settings - view
. settings = $ { {
darkMode : true ,
cloudflareToken : '' ,
cloudflareZoneId : '' ,
autoRenewCerts : true ,
renewalThreshold : 30 ,
acmeEmail : 'certs@example.com' ,
httpPort : 80 ,
httpsPort : 443 ,
forceHttps : true ,
} }
currentUser = "admin"
> < / s z - s e t t i n g s - v i e w >
< / div >
` ;
2026-02-20 13:29:07 +00:00
public static demoGroups = [ 'Auth & Settings' ] ;
2026-01-03 02:44:25 +00:00
@property ( { type : Object } )
public accessor settings : ISettings = {
darkMode : false ,
cloudflareToken : '' ,
cloudflareZoneId : '' ,
autoRenewCerts : true ,
renewalThreshold : 30 ,
acmeEmail : '' ,
httpPort : 80 ,
httpsPort : 443 ,
forceHttps : true ,
} ;
@property ( { type : String } )
public accessor currentUser : string = '' ;
public static styles = [
cssManager . defaultStyles ,
css `
: host {
display : block ;
}
. section {
background : $ { cssManager . bdTheme ( '#ffffff' , '#09090b' ) } ;
border : 1px solid $ { cssManager . bdTheme ( '#e4e4e7' , '#27272a' ) } ;
border - radius : 8px ;
padding : 20px ;
margin - bottom : 24px ;
}
. section - header {
margin - bottom : 16px ;
}
. section - title {
font - size : 16px ;
font - weight : 600 ;
color : $ { cssManager . bdTheme ( '#18181b' , '#fafafa' ) } ;
}
. section - subtitle {
font - size : 13px ;
color : $ { cssManager . bdTheme ( '#71717a' , '#a1a1aa' ) } ;
margin - top : 2px ;
}
. form - group {
margin - bottom : 16px ;
}
. form - group :last - child {
margin - bottom : 0 ;
}
. form - row {
display : flex ;
justify - content : space - between ;
align - items : center ;
padding : 12px 0 ;
border - bottom : 1px solid $ { cssManager . bdTheme ( '#f4f4f5' , '#27272a' ) } ;
}
. form - row :last - child {
border - bottom : none ;
}
. form - label - group {
display : flex ;
flex - direction : column ;
gap : 2px ;
}
. form - label {
font - size : 14px ;
font - weight : 500 ;
color : $ { cssManager . bdTheme ( '#18181b' , '#fafafa' ) } ;
}
. form - hint {
font - size : 12px ;
color : $ { cssManager . bdTheme ( '#71717a' , '#a1a1aa' ) } ;
}
. input - group {
display : flex ;
flex - direction : column ;
gap : 8px ;
}
. input - row {
display : grid ;
grid - template - columns : 1fr 1 fr ;
gap : 16px ;
}
input [ type = "text" ] ,
input [ type = "password" ] ,
input [ type = "email" ] ,
input [ type = "number" ] {
width : 100 % ;
padding : 8px 12 px ;
background : $ { cssManager . bdTheme ( '#ffffff' , '#09090b' ) } ;
border : 1px solid $ { cssManager . bdTheme ( '#e4e4e7' , '#27272a' ) } ;
border - radius : 6px ;
font - size : 14px ;
color : $ { cssManager . bdTheme ( '#18181b' , '#fafafa' ) } ;
outline : none ;
transition : border - color 200 ms ease ;
box - sizing : border - box ;
}
input :focus {
border - color : $ { cssManager . bdTheme ( '#3b82f6' , '#60a5fa' ) } ;
}
input : : placeholder {
color : $ { cssManager . bdTheme ( '#a1a1aa' , '#52525b' ) } ;
}
. toggle - switch {
position : relative ;
width : 44px ;
height : 24px ;
background : $ { cssManager . bdTheme ( '#e4e4e7' , '#27272a' ) } ;
border - radius : 9999px ;
cursor : pointer ;
transition : background 200 ms ease ;
}
. toggle - switch . active {
background : $ { cssManager . bdTheme ( '#2563eb' , '#3b82f6' ) } ;
}
. toggle - switch : : after {
content : '' ;
position : absolute ;
top : 2px ;
left : 2px ;
width : 20px ;
height : 20px ;
background : white ;
border - radius : 50 % ;
transition : transform 200 ms ease ;
}
. toggle - switch . active : : after {
transform : translateX ( 20 px ) ;
}
. password - section {
margin - top : 16px ;
padding - top : 16px ;
border - top : 1px solid $ { cssManager . bdTheme ( '#f4f4f5' , '#27272a' ) } ;
}
. password - title {
font - size : 14px ;
font - weight : 600 ;
color : $ { cssManager . bdTheme ( '#18181b' , '#fafafa' ) } ;
margin - bottom : 12px ;
}
. password - fields {
display : flex ;
flex - direction : column ;
gap : 12px ;
}
. field - label {
font - size : 13px ;
color : $ { cssManager . bdTheme ( '#71717a' , '#a1a1aa' ) } ;
margin - bottom : 4px ;
}
. actions {
display : flex ;
justify - content : flex - end ;
gap : 12px ;
padding - top : 16px ;
border - top : 1px solid $ { cssManager . bdTheme ( '#e4e4e7' , '#27272a' ) } ;
margin - top : 24px ;
}
. button {
padding : 10px 20 px ;
border - radius : 6px ;
font - size : 14px ;
font - weight : 500 ;
cursor : pointer ;
transition : all 200 ms ease ;
}
. button . secondary {
background : $ { cssManager . bdTheme ( '#ffffff' , '#09090b' ) } ;
border : 1px solid $ { cssManager . bdTheme ( '#e4e4e7' , '#27272a' ) } ;
color : $ { cssManager . bdTheme ( '#18181b' , '#fafafa' ) } ;
}
. button.secondary :hover {
background : $ { cssManager . bdTheme ( '#f4f4f5' , '#18181b' ) } ;
}
. button . primary {
background : $ { cssManager . bdTheme ( '#18181b' , '#fafafa' ) } ;
border : none ;
color : $ { cssManager . bdTheme ( '#fafafa' , '#18181b' ) } ;
}
. button.primary :hover {
opacity : 0.9 ;
}
` ,
] ;
public render ( ) : TemplateResult {
return html `
< div class = "section" >
< div class = "section-header" >
< div class = "section-title" > Appearance < / div >
< div class = "section-subtitle" > Customize the look and feel < / div >
< / div >
< div class = "form-row" >
< div class = "form-label-group" >
< span class = "form-label" > Dark Mode < / span >
< span class = "form-hint" > Toggle dark mode on or off < / span >
< / div >
< div class = "toggle-switch ${this.settings.darkMode ? 'active' : ''}" @ click = $ { ( ) = > this . toggleDarkMode ( ) } > < / div >
< / div >
< / div >
< div class = "section" >
< div class = "section-header" >
< div class = "section-title" > Cloudflare Integration < / div >
< div class = "section-subtitle" > Configure Cloudflare API for DNS management < / div >
< / div >
< div class = "input-group" >
< div class = "form-group" >
< div class = "field-label" > API Token < / div >
< input type = "password" placeholder = "Enter Cloudflare API token" .value = $ { this.settings.cloudflareToken } @ input = $ { ( e : Event ) = > this . updateSetting ( 'cloudflareToken' , ( e . target as HTMLInputElement ) . value ) } >
< / div >
< div class = "form-group" >
< div class = "field-label" > Zone ID ( Optional ) < / div >
< input type = "text" placeholder = "Default zone ID" .value = $ { this.settings.cloudflareZoneId } @ input = $ { ( e : Event ) = > this . updateSetting ( 'cloudflareZoneId' , ( e . target as HTMLInputElement ) . value ) } >
< / div >
< div class = "form-hint" > Get your API token from the Cloudflare dashboard with DNS edit permissions . < / div >
< / div >
< / div >
< div class = "section" >
< div class = "section-header" >
< div class = "section-title" > SSL / TLS Settings < / div >
< div class = "section-subtitle" > Configure certificate management < / div >
< / div >
< div class = "form-row" >
< div class = "form-label-group" >
< span class = "form-label" > Auto - Renew Certificates < / span >
< span class = "form-hint" > Automatically renew certificates before expiry < / span >
< / div >
< div class = "toggle-switch ${this.settings.autoRenewCerts ? 'active' : ''}" @ click = $ { ( ) = > this . toggleSetting ( 'autoRenewCerts' ) } > < / div >
< / div >
< div class = "form-group" style = "margin-top: 16px;" >
< div class = "field-label" > Renewal Threshold ( days ) < / div >
< input type = "number" .value = $ { String ( this.settings.renewalThreshold ) } @ input = $ { ( e : Event ) = > this . updateSetting ( 'renewalThreshold' , parseInt ( ( e . target as HTMLInputElement ) . value ) ) } >
< div class = "form-hint" > Renew certificates when they have fewer than this many days remaining . < / div >
< / div >
< div class = "form-group" >
< div class = "field-label" > ACME Email < / div >
< input type = "email" placeholder = "admin@example.com" .value = $ { this.settings.acmeEmail } @ input = $ { ( e : Event ) = > this . updateSetting ( 'acmeEmail' , ( e . target as HTMLInputElement ) . value ) } >
< div class = "form-hint" > Email address for Let ' s Encrypt notifications . < / div >
< / div >
< / div >
< div class = "section" >
< div class = "section-header" >
< div class = "section-title" > Network Settings < / div >
< div class = "section-subtitle" > Configure network and proxy settings < / div >
< / div >
< div class = "input-row" >
< div class = "form-group" >
< div class = "field-label" > HTTP Port < / div >
< input type = "number" .value = $ { String ( this.settings.httpPort ) } @ input = $ { ( e : Event ) = > this . updateSetting ( 'httpPort' , parseInt ( ( e . target as HTMLInputElement ) . value ) ) } >
< / div >
< div class = "form-group" >
< div class = "field-label" > HTTPS Port < / div >
< input type = "number" .value = $ { String ( this.settings.httpsPort ) } @ input = $ { ( e : Event ) = > this . updateSetting ( 'httpsPort' , parseInt ( ( e . target as HTMLInputElement ) . value ) ) } >
< / div >
< / div >
< div class = "form-row" >
< div class = "form-label-group" >
< span class = "form-label" > Force HTTPS < / span >
< span class = "form-hint" > Redirect all HTTP traffic to HTTPS < / span >
< / div >
< div class = "toggle-switch ${this.settings.forceHttps ? 'active' : ''}" @ click = $ { ( ) = > this . toggleSetting ( 'forceHttps' ) } > < / div >
< / div >
< / div >
< div class = "section" >
< div class = "section-header" >
< div class = "section-title" > Account < / div >
< div class = "section-subtitle" > Manage your account settings < / div >
< / div >
< div class = "form-group" >
< div class = "field-label" > Current User < / div >
< div style = "font-size: 14px; color: ${cssManager.bdTheme('#18181b', '#fafafa')};" > $ { this . currentUser || 'Unknown' } < / div >
< / div >
< div class = "password-section" >
< div class = "password-title" > Change Password < / div >
< div class = "password-fields" >
< div >
< div class = "field-label" > Current Password < / div >
< input type = "password" id = "currentPassword" >
< / div >
< div >
< div class = "field-label" > New Password < / div >
< input type = "password" id = "newPassword" >
< / div >
< div >
< div class = "field-label" > Confirm Password < / div >
< input type = "password" id = "confirmPassword" >
< / div >
< button class = "button secondary" style = "width: fit-content;" @ click = $ { ( ) = > this . handleChangePassword ( ) } > Update Password < / button >
< / div >
< / div >
< / div >
< div class = "actions" >
< button class = "button secondary" @ click = $ { ( ) = > this . handleReset ( ) } > Reset < / button >
< button class = "button primary" @ click = $ { ( ) = > this . handleSave ( ) } > Save Settings < / button >
< / div >
` ;
}
private toggleDarkMode() {
this . settings = { . . . this . settings , darkMode : ! this . settings . darkMode } ;
this . dispatchEvent ( new CustomEvent ( 'setting-change' , { detail : { key : 'darkMode' , value : this.settings.darkMode } , bubbles : true , composed : true } ) ) ;
}
private toggleSetting ( key : keyof ISettings ) {
( this . settings as any ) [ key ] = ! ( this . settings as any ) [ key ] ;
this . settings = { . . . this . settings } ;
}
private updateSetting ( key : keyof ISettings , value : any ) {
( this . settings as any ) [ key ] = value ;
this . settings = { . . . this . settings } ;
}
private handleChangePassword() {
const currentPassword = ( this . shadowRoot ? . getElementById ( 'currentPassword' ) as HTMLInputElement ) ? . value ;
const newPassword = ( this . shadowRoot ? . getElementById ( 'newPassword' ) as HTMLInputElement ) ? . value ;
const confirmPassword = ( this . shadowRoot ? . getElementById ( 'confirmPassword' ) as HTMLInputElement ) ? . value ;
this . dispatchEvent ( new CustomEvent ( 'change-password' , {
detail : { currentPassword , newPassword , confirmPassword } ,
bubbles : true ,
composed : true
} ) ) ;
}
private handleReset() {
this . dispatchEvent ( new CustomEvent ( 'reset' , { bubbles : true , composed : true } ) ) ;
}
private handleSave() {
this . dispatchEvent ( new CustomEvent ( 'save' , { detail : this.settings , bubbles : true , composed : true } ) ) ;
}
}