Skip to content

Commit 6b2cb4d

Browse files
committed
fix: polish notifications settings UI
1 parent 0715081 commit 6b2cb4d

3 files changed

Lines changed: 348 additions & 73 deletions

File tree

ui/src/styles/config.css

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,200 @@
903903
box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok) 14%, transparent);
904904
}
905905

906+
/* Notifications section */
907+
.settings-notifications {
908+
display: grid;
909+
gap: 14px;
910+
max-width: 920px;
911+
}
912+
913+
.settings-notifications__card {
914+
border: 1px solid var(--border);
915+
border-radius: var(--radius-lg);
916+
background: var(--bg-elevated);
917+
overflow: hidden;
918+
box-shadow: var(--shadow-sm);
919+
}
920+
921+
:root[data-theme-mode="light"] .settings-notifications__card {
922+
background: white;
923+
}
924+
925+
.settings-notifications__header {
926+
display: grid;
927+
grid-template-columns: auto minmax(0, 1fr) auto;
928+
align-items: start;
929+
gap: 14px;
930+
padding: 18px 20px;
931+
border-bottom: 1px solid var(--border);
932+
background: var(--bg-accent);
933+
}
934+
935+
:root[data-theme-mode="light"] .settings-notifications__header {
936+
background: var(--bg-hover);
937+
}
938+
939+
.settings-notifications__icon {
940+
display: inline-flex;
941+
align-items: center;
942+
justify-content: center;
943+
width: 34px;
944+
height: 34px;
945+
border-radius: var(--radius-md);
946+
background: var(--accent-subtle);
947+
color: var(--accent);
948+
flex: 0 0 auto;
949+
}
950+
951+
.settings-notifications__icon svg {
952+
width: 18px;
953+
height: 18px;
954+
stroke: currentColor;
955+
fill: none;
956+
}
957+
958+
.settings-notifications__copy {
959+
display: grid;
960+
gap: 4px;
961+
min-width: 0;
962+
}
963+
964+
.settings-notifications__title {
965+
margin: 0;
966+
font-size: 14px;
967+
font-weight: 650;
968+
letter-spacing: -0.015em;
969+
line-height: 1.35;
970+
color: var(--text-strong);
971+
}
972+
973+
.settings-notifications__hint {
974+
margin: 0;
975+
max-width: 58ch;
976+
font-size: 12px;
977+
line-height: 1.45;
978+
color: var(--muted);
979+
}
980+
981+
.settings-notifications__badge {
982+
display: inline-flex;
983+
align-items: center;
984+
justify-content: center;
985+
min-height: 24px;
986+
padding: 4px 9px;
987+
border-radius: var(--radius-full);
988+
border: 1px solid transparent;
989+
font-size: 11px;
990+
font-weight: 650;
991+
line-height: 1;
992+
white-space: nowrap;
993+
}
994+
995+
.settings-notifications__badge--accent {
996+
color: var(--accent);
997+
border-color: color-mix(in srgb, var(--accent) 28%, transparent);
998+
background: var(--accent-subtle);
999+
}
1000+
1001+
.settings-notifications__badge--ok {
1002+
color: var(--ok);
1003+
border-color: color-mix(in srgb, var(--ok) 28%, transparent);
1004+
background: var(--ok-subtle);
1005+
}
1006+
1007+
.settings-notifications__badge--danger {
1008+
color: var(--danger);
1009+
border-color: color-mix(in srgb, var(--danger) 28%, transparent);
1010+
background: var(--danger-subtle);
1011+
}
1012+
1013+
.settings-notifications__badge--muted {
1014+
color: var(--muted);
1015+
border-color: var(--border);
1016+
background: var(--bg);
1017+
}
1018+
1019+
.settings-notifications__body {
1020+
display: grid;
1021+
gap: 16px;
1022+
padding: 16px 18px 18px;
1023+
}
1024+
1025+
.settings-notifications__details {
1026+
display: grid;
1027+
grid-template-columns: repeat(3, minmax(0, 1fr));
1028+
gap: 10px;
1029+
}
1030+
1031+
.settings-notifications__detail {
1032+
display: grid;
1033+
gap: 6px;
1034+
min-width: 0;
1035+
padding: 12px 14px;
1036+
border: 1px solid var(--border);
1037+
border-radius: var(--radius-md);
1038+
background: var(--bg);
1039+
}
1040+
1041+
.settings-notifications__label {
1042+
font-size: 11px;
1043+
font-weight: 650;
1044+
letter-spacing: 0.04em;
1045+
text-transform: uppercase;
1046+
color: var(--muted);
1047+
}
1048+
1049+
.settings-notifications__value {
1050+
min-width: 0;
1051+
font-size: 13px;
1052+
font-weight: 600;
1053+
color: var(--text);
1054+
overflow-wrap: anywhere;
1055+
}
1056+
1057+
.settings-notifications__value--status {
1058+
display: inline-flex;
1059+
align-items: center;
1060+
gap: 8px;
1061+
}
1062+
1063+
.settings-notifications__dot {
1064+
width: 8px;
1065+
height: 8px;
1066+
border-radius: var(--radius-full);
1067+
background: var(--muted);
1068+
box-shadow: 0 0 0 4px color-mix(in srgb, var(--muted) 14%, transparent);
1069+
flex: 0 0 auto;
1070+
}
1071+
1072+
.settings-notifications__dot--ok {
1073+
background: var(--ok);
1074+
box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok) 14%, transparent);
1075+
}
1076+
1077+
.settings-notifications__actions {
1078+
display: flex;
1079+
align-items: center;
1080+
flex-wrap: wrap;
1081+
gap: 8px;
1082+
}
1083+
1084+
.settings-notifications__actions .btn svg {
1085+
width: 15px;
1086+
height: 15px;
1087+
}
1088+
1089+
.settings-notifications__callout {
1090+
width: 100%;
1091+
padding: 11px 13px;
1092+
border: 1px solid color-mix(in srgb, var(--danger) 22%, transparent);
1093+
border-radius: var(--radius-md);
1094+
background: var(--danger-subtle);
1095+
color: var(--text);
1096+
font-size: 12px;
1097+
line-height: 1.45;
1098+
}
1099+
9061100
.config-raw-field textarea {
9071101
min-height: 500px;
9081102
font-family: var(--mono);
@@ -2132,6 +2326,24 @@
21322326
text-align: left;
21332327
}
21342328

2329+
.settings-notifications__header {
2330+
grid-template-columns: auto minmax(0, 1fr);
2331+
padding: 16px;
2332+
}
2333+
2334+
.settings-notifications__badge {
2335+
grid-column: 2;
2336+
justify-self: start;
2337+
}
2338+
2339+
.settings-notifications__body {
2340+
padding: 14px 16px 16px;
2341+
}
2342+
2343+
.settings-notifications__details {
2344+
grid-template-columns: 1fr;
2345+
}
2346+
21352347
.config-section-card__header {
21362348
padding: 14px 16px;
21372349
}

ui/src/ui/views/config.browser.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,40 @@ describe("config view", () => {
387387
expect(onSectionChange).toHaveBeenCalledWith("__notifications__");
388388
});
389389

390+
it("renders Notifications with the shared settings card and button styles", () => {
391+
const onWebPushSubscribe = vi.fn();
392+
const { container } = renderConfigView({
393+
activeSection: "__notifications__",
394+
includeSections: ["channels", "messages", "__notifications__"],
395+
includeVirtualSections: true,
396+
onWebPushSubscribe,
397+
schema: {
398+
type: "object",
399+
properties: {
400+
channels: { type: "object", properties: {} },
401+
messages: { type: "object", properties: {} },
402+
},
403+
},
404+
webPush: {
405+
supported: true,
406+
permission: "default",
407+
subscribed: false,
408+
loading: false,
409+
},
410+
});
411+
412+
const card = queryRequired(container, ".settings-notifications__card", HTMLElement);
413+
expect(card.querySelector(".settings-notifications__badge")?.textContent?.trim()).toBe("Ready");
414+
415+
const enableButton = findButtonByText(container, "Enable notifications");
416+
expect(enableButton.classList.contains("btn")).toBe(true);
417+
expect(enableButton.classList.contains("primary")).toBe(true);
418+
expect(container.querySelector(".config-bar__btn")).toBeNull();
419+
420+
enableButton.click();
421+
expect(onWebPushSubscribe).toHaveBeenCalledOnce();
422+
});
423+
390424
it("resets config content scroll when switching top-tab sections", async () => {
391425
const { container } = renderConfigView({
392426
activeSection: "channels",

0 commit comments

Comments
 (0)