import { DeesElement, property, html, customElement, type TemplateResult, css, cssManager, unsafeCSS } from '@design.estate/dees-element'; import * as domtools from '@design.estate/dees-domtools'; import { fonts, colors, shadows, borderRadius, spacing, commonStyles } from '../styles/shared.styles.js'; import { demoFunc } from './upl-statuspage-footer.demo.js'; declare global { interface HTMLElementTagNameMap { 'upl-statuspage-footer': UplStatuspageFooter; } } @customElement('upl-statuspage-footer') export class UplStatuspageFooter extends DeesElement { // STATIC public static demo = demoFunc; // INSTANCE @property({ type: String }) public companyName: string = ''; @property({ type: String }) public legalUrl: string = ''; @property({ type: String }) public supportEmail: string = ''; @property({ type: String }) public statusPageUrl: string = ''; @property({ type: Boolean }) public whitelabel: boolean = false; @property({ type: Number }) public lastUpdated: number | null = null; @property({ type: Number }) public currentYear: number = new Date().getFullYear(); @property({ type: Array }) public socialLinks: Array<{ platform: string; url: string }> = []; @property({ type: Array }) public additionalLinks: Array<{ label: string; url: string }> = []; @property({ type: String }) public rssFeedUrl: string = ''; @property({ type: String }) public apiStatusUrl: string = ''; @property({ type: Boolean }) public loading: boolean = false; @property({ type: String }) public errorMessage: string | null = null; @property({ type: Boolean }) public offline: boolean = false; @property({ type: String }) public latestStatusUpdate: string = ''; @property({ type: Boolean }) public enableSubscribe: boolean = false; @property({ type: Boolean }) public enableReportIssue: boolean = false; @property({ type: Boolean }) public enableLanguageSelector: boolean = false; @property({ type: Boolean }) public enableThemeToggle: boolean = false; @property({ type: Array }) public languageOptions: Array<{ code: string; label: string }> = []; @property({ type: String }) public currentLanguage: string = 'en'; @property({ type: String }) public currentTheme: string = 'light'; @property({ type: Number }) public subscriberCount: number = 0; @property({ type: Object }) public customBranding: { primaryColor?: string; logoUrl?: string; footerText?: string } | null = null; constructor() { super(); } public static styles = [ domtools.elementBasic.staticStyles, commonStyles, css` :host { display: block; background: ${colors.background.primary}; font-family: ${unsafeCSS(fonts.base)}; color: ${colors.text.primary}; font-size: 14px; border-top: 1px solid ${colors.border.default}; } .container { max-width: 1200px; margin: 0 auto; padding: ${unsafeCSS(spacing['2xl'])} ${unsafeCSS(spacing.lg)}; } .footer-content { display: flex; flex-direction: column; gap: ${unsafeCSS(spacing.xl)}; } .footer-main { display: grid; grid-template-columns: 1fr auto; gap: ${unsafeCSS(spacing['2xl'])}; align-items: start; } .company-info { display: flex; flex-direction: column; gap: ${unsafeCSS(spacing.lg)}; } .company-name { font-size: 20px; font-weight: 600; color: ${colors.text.primary}; } .company-links { display: flex; flex-wrap: wrap; gap: ${unsafeCSS(spacing.lg)}; } .footer-link { color: ${colors.text.secondary}; text-decoration: none; transition: color 0.2s ease; font-size: 14px; } .footer-link:hover { color: ${colors.text.primary}; text-decoration: underline; } .footer-actions { display: flex; flex-direction: column; gap: ${unsafeCSS(spacing.md)}; } .action-button { padding: ${unsafeCSS(spacing.sm)} ${unsafeCSS(spacing.md)}; border: 1px solid ${colors.border.default}; background: transparent; border-radius: ${unsafeCSS(borderRadius.base)}; cursor: pointer; text-align: center; transition: all 0.2s ease; white-space: nowrap; font-size: 14px; font-weight: 500; color: ${colors.text.primary}; font-family: ${unsafeCSS(fonts.base)}; } .action-button:hover { background: ${colors.background.secondary}; border-color: ${colors.border.muted}; transform: translateY(-1px); } .action-button:active { transform: translateY(0); } .footer-bottom { display: flex; justify-content: space-between; align-items: center; padding-top: ${unsafeCSS(spacing.xl)}; margin-top: ${unsafeCSS(spacing.xl)}; border-top: 1px solid ${colors.border.default}; } .footer-meta { display: flex; gap: ${unsafeCSS(spacing.lg)}; align-items: center; flex-wrap: wrap; } .social-links { display: flex; gap: ${unsafeCSS(spacing.md)}; align-items: center; } .social-link { display: inline-block; width: 20px; height: 20px; opacity: 0.7; transition: all 0.2s ease; color: ${colors.text.secondary}; } .social-link:hover { opacity: 1; color: ${colors.text.primary}; transform: translateY(-1px); } .social-link svg { width: 100%; height: 100%; fill: currentColor; } .copyright { font-size: 14px; color: ${colors.text.secondary}; } .last-updated { font-size: 13px; color: ${colors.text.muted}; } .powered-by { font-size: 13px; color: ${colors.text.muted}; text-align: right; } .powered-by a { color: ${colors.text.secondary}; text-decoration: none; transition: color 0.2s ease; } .powered-by a:hover { color: ${colors.text.primary}; text-decoration: underline; } .status-update { padding: ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.lg)}; background: ${colors.background.muted}; border-radius: ${unsafeCSS(borderRadius.base)}; font-size: 14px; margin-bottom: ${unsafeCSS(spacing.lg)}; line-height: 1.6; } .language-selector { position: relative; } .language-selector select { padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.sm)}; border: 1px solid ${colors.border.default}; background: ${colors.background.primary}; color: ${colors.text.primary}; border-radius: ${unsafeCSS(borderRadius.base)}; font-size: 14px; cursor: pointer; font-family: ${unsafeCSS(fonts.base)}; } .theme-toggle { cursor: pointer; padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.sm)}; border: 1px solid ${colors.border.default}; background: transparent; border-radius: ${unsafeCSS(borderRadius.base)}; font-size: 14px; transition: all 0.2s ease; color: ${colors.text.primary}; font-family: ${unsafeCSS(fonts.base)}; } .theme-toggle:hover { background: ${colors.background.secondary}; border-color: ${colors.border.muted}; } .subscriber-count { font-size: 12px; color: ${colors.text.muted}; margin-top: ${unsafeCSS(spacing.xs)}; } .error-message { padding: ${unsafeCSS(spacing.md)}; background: ${cssManager.bdTheme('#fee9e9', '#7f1d1d')}; color: ${cssManager.bdTheme('#dc2626', '#fca5a5')}; border-radius: ${unsafeCSS(borderRadius.base)}; margin-bottom: ${unsafeCSS(spacing.lg)}; font-size: 14px; border: 1px solid ${cssManager.bdTheme('#fecaca', '#991b1b')}; } .offline-indicator { display: inline-flex; align-items: center; gap: ${unsafeCSS(spacing.xs)}; padding: ${unsafeCSS(spacing.xs)} ${unsafeCSS(spacing.md)}; background: ${colors.status.degraded}; color: white; border-radius: ${unsafeCSS(borderRadius.full)}; font-size: 13px; font-weight: 500; } .loading-skeleton { height: 200px; background: ${cssManager.bdTheme( 'linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%)', 'linear-gradient(90deg, #1f1f1f 25%, #262626 50%, #1f1f1f 75%)' )}; background-size: 200% 100%; animation: loading 1.5s infinite; border-radius: ${unsafeCSS(borderRadius.md)}; } @keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .additional-links { display: flex; flex-wrap: wrap; gap: ${unsafeCSS(spacing.md)}; margin-top: ${unsafeCSS(spacing.md)}; } .additional-link { font-size: 14px; color: ${colors.text.secondary}; text-decoration: none; transition: color 0.2s ease; } .additional-link:hover { color: ${colors.text.primary}; text-decoration: underline; } @media (max-width: 640px) { .container { padding: ${unsafeCSS(spacing.xl)} ${unsafeCSS(spacing.md)}; } .footer-main { grid-template-columns: 1fr; gap: ${unsafeCSS(spacing.xl)}; } .footer-bottom { flex-direction: column; gap: ${unsafeCSS(spacing.lg)}; align-items: start; } .footer-meta { flex-direction: column; align-items: start; gap: ${unsafeCSS(spacing.md)}; } .company-links { flex-direction: column; gap: ${unsafeCSS(spacing.sm)}; } .powered-by { text-align: left; } } ` ] public render(): TemplateResult { if (this.loading) { return html`