Reduce manage page redundant labels #13

Merged
nalalangan merged 1 commits from feature/reduce-manage-page-redundancy into feature-custom-store-locations 2026-05-31 18:49:06 -09:00
4 changed files with 24 additions and 39 deletions
Showing only changes of commit 0ef35c3f92 - Show all commits

View File

@ -162,6 +162,8 @@ For `app/api/**/[param]/route.ts`:
- Touch: long-press affordance for item-level actions when no visible button.
- Mouse: hover affordance on interactive rows/cards.
- Tap targets remain >= 40px on mobile.
- Avoid redundant nearby labels. If a tab, section title, count chip, row label, or control state already communicates the meaning, do not repeat it with an eyebrow label, explanatory zero-state sentence, or duplicate card label.
- Prefer compact label/value rows for dense settings controls instead of stacked labels with large vertical gaps.
- Modal overlays must close on outside click/tap.
- For every frontend action that manipulates database state, show a toast/bubble notification with basic outcome details (action + target + success/failure).
- Frontend destructive actions should use the shared `ConfirmSlideModal` pattern instead of browser `confirm()` unless there is a documented exception.

View File

@ -402,7 +402,6 @@ export default function ManageHousehold() {
<section key="household-name" className="manage-section">
<div className="manage-section-header">
<div>
<p className="manage-section-eyebrow">Household</p>
<h2>Identity</h2>
</div>
</div>
@ -447,7 +446,6 @@ export default function ManageHousehold() {
<section key="join-and-invites" className="manage-section">
<div className="manage-section-header">
<div>
<p className="manage-section-eyebrow">Entry Rules</p>
<h2>Invite Links</h2>
</div>
</div>
@ -471,9 +469,7 @@ export default function ManageHousehold() {
{inviteLoading ? (
<p>Loading invite settings...</p>
) : pendingRequests.length === 0 ? (
<p className="section-description">No pending join requests right now.</p>
) : (
) : pendingRequests.length > 0 ? (
<div className="pending-requests-list">
{pendingRequests.map((request) => {
const requesterLabel = getRequesterLabel(request);
@ -509,7 +505,7 @@ export default function ManageHousehold() {
);
})}
</div>
)}
) : null}
<div className="invite-controls">
<label>
@ -583,7 +579,6 @@ export default function ManageHousehold() {
<section key="members" className="manage-section">
<div className="manage-section-header">
<div>
<p className="manage-section-eyebrow">People</p>
<h2>Members ({members.length})</h2>
</div>
</div>
@ -630,7 +625,6 @@ export default function ManageHousehold() {
>
<div className="member-actions-modal-header">
<div className="member-actions-modal-copy">
<p className="manage-section-eyebrow">Member</p>
<h3 id="member-actions-title">{selectedMember.username}</h3>
<span className={`member-role member-role-${selectedMember.role}`}>
{selectedRoleMeta.icon} {selectedRoleMeta.label}
@ -683,7 +677,6 @@ export default function ManageHousehold() {
<section key="danger-zone" className="manage-section danger-zone">
<div className="manage-section-header">
<div>
<p className="manage-section-eyebrow">Final Actions</p>
<h2>Danger Zone</h2>
</div>
{isMemberOnly ? (

View File

@ -39,20 +39,11 @@ body.dark-mode .manage-section {
}
.manage-section-header h2 {
margin: 0.15rem 0 0;
margin: 0;
font-size: 1.2rem;
color: var(--text-primary);
}
.manage-section-eyebrow {
margin: 0;
color: var(--primary);
font-size: 0.74rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.section-description {
color: var(--text-secondary);
font-size: 0.92rem;
@ -131,7 +122,7 @@ body.dark-mode .edit-name-form input {
}
.manage-household-join-policy-toggle {
margin-bottom: 0.2rem;
margin-bottom: 0;
}
.pending-requests-summary {
@ -243,15 +234,16 @@ body.dark-mode .pending-request-card {
.invite-controls {
display: grid;
grid-template-columns: repeat(2, minmax(140px, 180px)) auto;
gap: 0.8rem;
align-items: end;
grid-template-columns: 1fr;
gap: 0.45rem;
align-items: stretch;
}
.invite-controls label {
display: flex;
flex-direction: column;
gap: 0.35rem;
display: grid;
grid-template-columns: 4.5rem minmax(0, 1fr);
gap: 0.75rem;
align-items: center;
color: var(--text-primary);
font-size: 0.9rem;
}
@ -265,14 +257,19 @@ body.dark-mode .pending-request-card {
}
.invite-controls select {
min-width: 120px;
width: 100%;
min-width: 0;
border: 1px solid var(--border);
border-radius: var(--border-radius-md);
padding: 0.7rem 0.75rem;
padding: 0.62rem 0.75rem;
background: rgba(255, 255, 255, 1);
color: var(--text-primary);
}
.invite-controls .btn-primary {
margin-top: 0.2rem;
}
[data-theme="dark"] .invite-controls select,
body.dark-mode .invite-controls select {
background: rgba(12, 19, 30, 0.92);
@ -527,8 +524,7 @@ body.dark-mode .danger-zone {
border-color: color-mix(in srgb, var(--danger) 42%, transparent);
}
.danger-zone h2,
.danger-zone .manage-section-eyebrow {
.danger-zone h2 {
color: var(--danger);
}
@ -641,14 +637,6 @@ body.dark-mode .danger-zone {
/* Responsive */
@media (max-width: 900px) {
.invite-controls {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.invite-controls .btn-primary {
grid-column: 1 / -1;
}
.invite-link-card {
grid-template-columns: 1fr;
}

View File

@ -176,7 +176,9 @@ test("household management shows pending invite approvals and can approve them",
await expect(page.locator(".action-toast.action-toast-success")).toContainText("Approved join request");
await expect(page.locator(".action-toast.action-toast-success")).toContainText("Pending Pal");
await expect(page.getByText("No pending join requests right now.")).toBeVisible();
await expect(page.getByText("No pending join requests right now.")).toHaveCount(0);
await expect(page.locator(".pending-requests-summary")).toContainText("0");
await expect(page.locator(".manage-section-eyebrow")).toHaveCount(0);
await expect(page.getByText("Members (2)")).toBeVisible();
});