🎨 ✨ 🚀 Overhaul UI/UX with comprehensive design system improvements
✨ Features added: - 🔍 Implement functional search in header navigation - ⚙️ Add basic user settings page - 📱 Make dashboard fully responsive 🔧 Enhancements: - 🎭 Standardize CSS with consistent theming across components - 🧹 Remove unused CSS for better performance - 📊 Improve dashboard layout and visual hierarchy - 📦 Redesign last block widget for better usability 💅 This commit introduces a cohesive design system with functional design-token components for a more ✨ polished user experience.
This commit is contained in:
parent
5afeb4d01a
commit
dc9abee715
49 changed files with 4176 additions and 2468 deletions
|
@ -1,11 +1,16 @@
|
|||
import { getLastBlockHeight } from '@utils/lastBlockHeight';
|
||||
import formatDateTime from '@/utils/formatDateTime';
|
||||
import { type LastBlock, getLastBlock } from '@/utils/lastBlock';
|
||||
import { LitElement, css, html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
import '@components/LoadingView';
|
||||
import '@components/General/Tooltip';
|
||||
import '@components/General/Fieldset';
|
||||
|
||||
@customElement('arx-bitcoin-block-widget')
|
||||
export class BitcoinBlockWidget extends LitElement {
|
||||
@state()
|
||||
private lastBlock: number | null = null;
|
||||
private lastBlock: LastBlock | null = null;
|
||||
|
||||
@state()
|
||||
private isLoading = true;
|
||||
|
@ -13,15 +18,16 @@ export class BitcoinBlockWidget extends LitElement {
|
|||
@state()
|
||||
private error: string | null = null;
|
||||
|
||||
private REFRESH_INTERVAL = 5000;
|
||||
private REFRESH_INTERVAL = 30000;
|
||||
|
||||
@state()
|
||||
private timer: number | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.loadBlockHeight();
|
||||
this.timer = window.setInterval(this.loadBlockHeight, this.REFRESH_INTERVAL);
|
||||
this.loadBlock = this.loadBlock.bind(this);
|
||||
this.loadBlock();
|
||||
this.timer = window.setInterval(this.loadBlock, this.REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
|
@ -29,70 +35,138 @@ export class BitcoinBlockWidget extends LitElement {
|
|||
if (this.timer) clearInterval(this.timer);
|
||||
}
|
||||
|
||||
async loadBlockHeight() {
|
||||
async loadBlock() {
|
||||
this.isLoading = true;
|
||||
try {
|
||||
const response = await getLastBlockHeight();
|
||||
this.lastBlock = response.height;
|
||||
const response = await getLastBlock();
|
||||
this.lastBlock = response;
|
||||
this.error = null;
|
||||
} catch (error) {
|
||||
this.error = 'Failed to load block height';
|
||||
this.error = 'Failed to load block data';
|
||||
console.error(error);
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private formatBlockId(id: string): string {
|
||||
if (!id) return '';
|
||||
if (id.length <= 16) return id;
|
||||
return `${id.substring(0, 8)}...${id.substring(id.length - 8)}`;
|
||||
}
|
||||
|
||||
private formatSize(size: number): string {
|
||||
if (size < 1024) return `${size} Bytes`;
|
||||
if (size < 1024 * 1024) return `${(size / 1024).toFixed(2)} KB`;
|
||||
return `${(size / (1024 * 1024)).toFixed(2)} MB`;
|
||||
}
|
||||
|
||||
private copyToClipboard(text: string) {
|
||||
navigator.clipboard.writeText(text).catch((err) => {
|
||||
console.error('Could not copy text: ', err);
|
||||
});
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
.error {
|
||||
color: #dc3545;
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
background: #f8d7da;
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading {
|
||||
.widget-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: var(--border) solid var(--color-base-300);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.block-height {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 1.25rem;
|
||||
.widget-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
color: var(--color-base-content);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.bitcoin-icon {
|
||||
color: var(--color-warning);
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--color-error-content);
|
||||
padding: 12px;
|
||||
border-radius: var(--radius-field);
|
||||
background: var(--color-error);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
override render() {
|
||||
if (this.error) {
|
||||
return html`<div class="error">${this.error}</div>`;
|
||||
}
|
||||
private renderError() {
|
||||
return html`<div class="error">${this.error}</div>`;
|
||||
}
|
||||
|
||||
if (this.isLoading) {
|
||||
return html`
|
||||
<div class="loading">
|
||||
<span class="loader"></span>
|
||||
Loading latest block...
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
private renderLoading() {
|
||||
return html`<arx-loading-view></arx-loading-view>`;
|
||||
}
|
||||
|
||||
private renderEmptyState() {
|
||||
return html`<div class="error">No block data available</div>`;
|
||||
}
|
||||
|
||||
private renderBlockData() {
|
||||
if (!this.lastBlock) return this.renderEmptyState();
|
||||
|
||||
return html`
|
||||
<div class="block-height">
|
||||
<span class="label">Last Block:</span>
|
||||
<span class="value">${this.lastBlock?.toLocaleString()}</span>
|
||||
<arx-fieldset legend="Height">
|
||||
<strong>${this.lastBlock.height.toLocaleString()}</strong>
|
||||
</arx-fieldset>
|
||||
|
||||
<arx-fieldset legend="Block ID">
|
||||
<arx-tooltip
|
||||
content="Click to copy full ID"
|
||||
@click=${() => this.copyToClipboard(this.lastBlock!.id)}
|
||||
>
|
||||
<strong>${this.formatBlockId(this.lastBlock.id)}</strong>
|
||||
</arx-tooltip>
|
||||
</arx-fieldset>
|
||||
|
||||
<arx-fieldset legend="Timestamp">
|
||||
<strong>${formatDateTime(this.lastBlock.timestamp * 1000)}</strong>
|
||||
</arx-fieldset>
|
||||
|
||||
<arx-fieldset legend="Size">
|
||||
<strong>
|
||||
${this.formatSize(this.lastBlock.size)}
|
||||
(${this.lastBlock.tx_count.toLocaleString()} txs)
|
||||
</strong>
|
||||
</arx-fieldset>
|
||||
`;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="widget-header">
|
||||
<h3 class="widget-title">
|
||||
<span class="bitcoin-icon">₿</span> Bitcoin Latest Block
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
${when(
|
||||
this.error,
|
||||
() => this.renderError(),
|
||||
() =>
|
||||
when(
|
||||
this.isLoading && !this.lastBlock,
|
||||
() => this.renderLoading(),
|
||||
() => this.renderBlockData(),
|
||||
),
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue