feat: add profile sync functionality and redesign desktop client UI
Add sync_profile command to fetch latest profile from backend without re-enrollment. Add DeviceView struct to EnrollResponse. Replace hardcoded "just now" timestamp with now_label helper using Unix epoch seconds. Add sync button to UI with loading state. Redesign client interface with top strip containing brand lockup and action buttons, hero surface with profile metadata tiles, body grid with login/status panels and resources sidebar
This commit is contained in:
@@ -1,111 +1,329 @@
|
||||
:root {
|
||||
font-family: "Segoe UI", "SF Pro Text", sans-serif;
|
||||
color: #f5f7fb;
|
||||
font-family: "Segoe UI", "SF Pro Text", "Helvetica Neue", sans-serif;
|
||||
color: #eef4ff;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(79, 208, 164, 0.18), transparent 25%),
|
||||
linear-gradient(180deg, #08111f 0%, #0d1727 100%);
|
||||
radial-gradient(circle at 12% 10%, rgba(50, 196, 167, 0.2), transparent 24%),
|
||||
radial-gradient(circle at 90% 18%, rgba(89, 133, 255, 0.16), transparent 22%),
|
||||
linear-gradient(180deg, #07101c 0%, #0c1524 100%);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.client-shell {
|
||||
min-height: 100vh;
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.app-frame {
|
||||
width: min(1120px, 100%);
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: 24px;
|
||||
padding: 32px 20px;
|
||||
gap: 22px;
|
||||
}
|
||||
|
||||
.hero,
|
||||
.panel {
|
||||
width: min(560px, 100%);
|
||||
.top-strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
.brand-lockup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.brand-lockup img {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
.brand-copy {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
color: #74e0b8;
|
||||
margin: 0;
|
||||
color: #75e3ba;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.78rem;
|
||||
font-size: 0.74rem;
|
||||
}
|
||||
|
||||
.lede {
|
||||
color: #a9b8d3;
|
||||
.brand-copy h1,
|
||||
.hero-copy h2,
|
||||
.status-panel h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel {
|
||||
.brand-copy p,
|
||||
.hero-copy p,
|
||||
.status-panel p,
|
||||
.surface-header p,
|
||||
.detail-card span,
|
||||
.profile-card span {
|
||||
margin: 0;
|
||||
color: #9eb1d1;
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.shell-button,
|
||||
.shell-button-secondary {
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 12px 18px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: 160ms ease;
|
||||
}
|
||||
|
||||
.shell-button {
|
||||
background: linear-gradient(135deg, #74e0b8 0%, #1fb67a 100%);
|
||||
color: #04131a;
|
||||
}
|
||||
|
||||
.shell-button-secondary {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: #eef4ff;
|
||||
border: 1px solid rgba(177, 197, 229, 0.16);
|
||||
}
|
||||
|
||||
.shell-button:disabled,
|
||||
.shell-button-secondary:disabled {
|
||||
opacity: 0.58;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.hero-surface,
|
||||
.surface,
|
||||
.status-panel,
|
||||
.login-panel {
|
||||
background: rgba(11, 20, 35, 0.78);
|
||||
border: 1px solid rgba(177, 197, 229, 0.12);
|
||||
box-shadow: 0 24px 70px rgba(2, 8, 18, 0.32);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.hero-surface {
|
||||
border-radius: 30px;
|
||||
padding: 28px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr 0.8fr;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.hero-copy h2 {
|
||||
font-size: clamp(2rem, 4vw, 3.6rem);
|
||||
line-height: 1.04;
|
||||
max-width: 10ch;
|
||||
}
|
||||
|
||||
.hero-copy p {
|
||||
max-width: 56ch;
|
||||
font-size: 1.03rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.hero-meta {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.meta-tile {
|
||||
padding: 18px;
|
||||
border-radius: 22px;
|
||||
background: linear-gradient(180deg, rgba(117, 227, 186, 0.08), rgba(117, 227, 186, 0.02));
|
||||
border: 1px solid rgba(117, 227, 186, 0.16);
|
||||
}
|
||||
|
||||
.meta-tile strong {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.body-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.3fr 0.7fr;
|
||||
gap: 22px;
|
||||
}
|
||||
|
||||
.login-panel,
|
||||
.status-panel {
|
||||
border-radius: 28px;
|
||||
padding: 24px;
|
||||
border-radius: 24px;
|
||||
background: rgba(12, 22, 38, 0.84);
|
||||
border: 1px solid rgba(167, 185, 219, 0.14);
|
||||
box-shadow: 0 24px 60px rgba(2, 8, 18, 0.36);
|
||||
backdrop-filter: blur(16px);
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
form {
|
||||
.status-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.status-state {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: #c8d6ee;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
background: #ff8a7d;
|
||||
}
|
||||
|
||||
.status-dot.online {
|
||||
background: #74e0b8;
|
||||
}
|
||||
|
||||
.surface {
|
||||
border-radius: 24px;
|
||||
padding: 18px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.surface-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.surface-header h4 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.status-grid,
|
||||
.profile-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.detail-card,
|
||||
.profile-card {
|
||||
padding: 14px;
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(177, 197, 229, 0.1);
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.detail-card strong,
|
||||
.profile-card strong {
|
||||
font-size: 1rem;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.resource-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.resource-list li {
|
||||
padding: 10px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(117, 227, 186, 0.08);
|
||||
border: 1px solid rgba(117, 227, 186, 0.15);
|
||||
color: #dffaf0;
|
||||
}
|
||||
|
||||
.login-panel form {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
label {
|
||||
.login-panel label {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
color: #c2cfe5;
|
||||
}
|
||||
|
||||
input {
|
||||
border: 1px solid rgba(167, 185, 219, 0.16);
|
||||
background: rgba(7, 14, 27, 0.85);
|
||||
.login-panel input {
|
||||
border: 1px solid rgba(177, 197, 229, 0.16);
|
||||
background: rgba(7, 14, 27, 0.9);
|
||||
color: #f5f7fb;
|
||||
border-radius: 14px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 16px;
|
||||
padding: 15px 16px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 13px 18px;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #74e0b8 0%, #1fb67a 100%);
|
||||
color: #04141a;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #ffb7b7;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.status-row,
|
||||
.details {
|
||||
.login-card-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.details div {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
.error {
|
||||
padding: 12px 14px;
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 115, 115, 0.08);
|
||||
border: 1px solid rgba(255, 115, 115, 0.16);
|
||||
color: #ffc3c3;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.details span {
|
||||
color: #9db0cf;
|
||||
}
|
||||
@media (max-width: 960px) {
|
||||
.hero-surface,
|
||||
.body-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.resource-list {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
.top-strip,
|
||||
.status-top,
|
||||
.surface-header {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.status-grid,
|
||||
.profile-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user