Merge pull request 'Reduce manage page redundant labels' (#13) from feature/reduce-manage-page-redundancy into feature-custom-store-locations
This commit is contained in:
commit
737aa6d66e
@ -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.
|
||||
|
||||
@ -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 ? (
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user