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 ;
}
2026-04-07 22:27:23 +00:00
dees - tile {
display : block ;
2026-01-03 02:44:25 +00:00
margin - bottom : 24px ;
}
. section - header {
2026-04-07 22:27:23 +00:00
height : 36px ;
display : flex ;
align - items : center ;
padding : 0 16 px ;
width : 100 % ;
box - sizing : border - box ;
}
. section - heading {
flex : 1 ;
display : flex ;
align - items : baseline ;
gap : 8px ;
min - width : 0 ;
2026-01-03 02:44:25 +00:00
}
. section - title {
2026-04-07 22:27:23 +00:00
font - weight : 500 ;
font - size : 13px ;
letter - spacing : - 0.01 em ;
color : var ( -- dees - color - text - secondary ) ;
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
2026-01-03 02:44:25 +00:00
}
. section - subtitle {
2026-04-07 22:27:23 +00:00
font - size : 12px ;
color : var ( -- dees - color - text - muted ) ;
letter - spacing : - 0.01 em ;
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
}
. section - content {
padding : 20px ;
}
. section - footer {
display : flex ;
flex - direction : row ;
justify - content : flex - end ;
align - items : center ;
gap : 0 ;
height : 36px ;
width : 100 % ;
box - sizing : border - box ;
}
. tile - button {
padding : 0 16 px ;
height : 100 % ;
text - align : center ;
font - size : 12px ;
font - weight : 500 ;
cursor : pointer ;
user - select : none ;
transition : all 0.15 s ease ;
background : transparent ;
border : none ;
border - left : 1px solid var ( -- dees - color - border - subtle ) ;
color : var ( -- dees - color - text - muted ) ;
white - space : nowrap ;
display : flex ;
align - items : center ;
gap : 6px ;
}
. tile - button :first - child {
border - left : none ;
}
. tile - button :hover {
background : var ( -- dees - color - hover ) ;
color : var ( -- dees - color - text - primary ) ;
}
. tile - button . primary {
color : $ { cssManager . bdTheme ( 'hsl(217.2 91.2% 59.8%)' , 'hsl(213.1 93.9% 67.8%)' ) } ;
font - weight : 600 ;
}
. tile - button.primary :hover {
background : $ { cssManager . bdTheme ( 'hsl(217.2 91.2% 59.8% / 0.08)' , 'hsl(213.1 93.9% 67.8% / 0.08)' ) } ;
color : $ { cssManager . bdTheme ( 'hsl(217.2 91.2% 50%)' , 'hsl(213.1 93.9% 75%)' ) } ;
2026-01-03 02:44:25 +00:00
}
. 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 ;
}
` ,
] ;
public render ( ) : TemplateResult {
return html `
2026-04-07 22:27:23 +00:00
< dees - tile >
< div slot = "header" class = "section-header" >
< div class = "section-heading" >
< span class = "section-title" > Appearance < / span >
< span class = "section-subtitle" > Customize the look and feel < / span >
< / div >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< div class = "section-content" >
< 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 >
2026-01-03 02:44:25 +00:00
< / div >
< / div >
2026-04-07 22:27:23 +00:00
< / d e e s - t i l e >
2026-01-03 02:44:25 +00:00
2026-04-07 22:27:23 +00:00
< dees - tile >
< div slot = "header" class = "section-header" >
< div class = "section-heading" >
< span class = "section-title" > Cloudflare Integration < / span >
< span class = "section-subtitle" > Configure Cloudflare API for DNS management < / span >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< / div >
< div class = "section-content" >
< 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 >
2026-01-03 02:44:25 +00:00
< / div >
< / div >
2026-04-07 22:27:23 +00:00
< / d e e s - t i l e >
2026-01-03 02:44:25 +00:00
2026-04-07 22:27:23 +00:00
< dees - tile >
< div slot = "header" class = "section-header" >
< div class = "section-heading" >
< span class = "section-title" > SSL / TLS Settings < / span >
< span class = "section-subtitle" > Configure certificate management < / span >
< / div >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< div class = "section-content" >
< 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 >
2026-01-03 02:44:25 +00:00
< / div >
< / div >
2026-04-07 22:27:23 +00:00
< / d e e s - t i l e >
< dees - tile >
< div slot = "header" class = "section-header" >
< div class = "section-heading" >
< span class = "section-title" > Network Settings < / span >
< span class = "section-subtitle" > Configure network and proxy settings < / span >
< / div >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< div class = "section-content" >
< 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 >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< / d e e s - t i l e >
2026-01-03 02:44:25 +00:00
2026-04-07 22:27:23 +00:00
< dees - tile >
< div slot = "header" class = "section-header" >
< div class = "section-heading" >
< span class = "section-title" > Account < / span >
< span class = "section-subtitle" > Manage your account settings < / span >
< / div >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< div class = "section-content" >
2026-01-03 02:44:25 +00:00
< div class = "form-group" >
2026-04-07 22:27:23 +00:00
< div class = "field-label" > Current User < / div >
< div style = "font-size: 14px; color: ${cssManager.bdTheme('#18181b', '#fafafa')};" > $ { this . currentUser || 'Unknown' } < / div >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< 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 >
< / div >
2026-01-03 02:44:25 +00:00
< / div >
< / div >
2026-04-07 22:27:23 +00:00
< div slot = "footer" class = "section-footer" >
< button class = "tile-button" @ click = $ { ( ) = > this . handleChangePassword ( ) } > Update Password < / button >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< / d e e s - t i l e >
2026-01-03 02:44:25 +00:00
2026-04-07 22:27:23 +00:00
< dees - tile >
< div class = "section-content" style = "padding: 12px 16px; text-align: center; color: var(--dees-color-text-muted); font-size: 12px;" >
Save your changes or reset to defaults .
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< div slot = "footer" class = "section-footer" >
< button class = "tile-button" @ click = $ { ( ) = > this . handleReset ( ) } > Reset < / button >
< button class = "tile-button primary" @ click = $ { ( ) = > this . handleSave ( ) } > Save Settings < / button >
2026-01-03 02:44:25 +00:00
< / div >
2026-04-07 22:27:23 +00:00
< / d e e s - t i l e >
2026-01-03 02:44:25 +00:00
` ;
}
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 } ) ) ;
}
}