Save view bar
When someone changes a shared View's filters, sort, group, columns or search, Discard changes and Save for everyone appear in the toolbar until they choose.
Playground
Change the controls to try each prop. The code under the preview lists only the props you changed.
import { SaveViewBar } from '@/ui/save-view-bar';<SaveViewBar />In the toolbar
The pair sits between search and Display: a quiet Discard changes, then Save for everyone as the secondary button. Add the filter to make the View unsaved, then save or discard. A save ends in a toast and the pair goes.
import { FunnelSimpleIcon, SlidersHorizontalIcon } from '@phosphor-icons/react';
import { useState } from 'react';
import { Button } from '@/ui/button';
import { SaveViewBar, ViewSaveStatus } from '@/ui/save-view-bar';
import { toast } from '@/ui/toast';
// Add a filter to make the View unsaved, then save or discard it.
export default function SaveViewBarInTheToolbar() {
const [dirty, setDirty] = useState(false);
const [saving, setSaving] = useState(false);
const save = () => {
setSaving(true);
setTimeout(() => {
setSaving(false);
setDirty(false);
toast.add({ type: 'success', title: 'View saved for everyone' });
}, 1200);
};
return (
<div className="flex h-11 w-full items-center justify-between gap-100 border-b border-border bg-surface px-150">
<Button variant="ghost" selected={dirty} onClick={() => setDirty(true)}>
<FunnelSimpleIcon />
{dirty ? 'Stage is CV sent' : 'Filter'}
</Button>
<div className="flex items-center gap-100">
<ViewSaveStatus status={saving ? 'saving' : 'idle'} />
<SaveViewBar dirty={dirty} saving={saving} onDiscard={() => setDirty(false)} onSave={save} />
<Button variant="ghost">
<SlidersHorizontalIcon />
Display
</Button>
</div>
</div>
);
}Saving
While the save runs, Save for everyone disables and ignores more presses, and the status line left of search says so.
import { SaveViewBar, ViewSaveStatus } from '@/ui/save-view-bar';
// While the save runs, Save for everyone disables and the status line says so.
export default function SaveViewBarSaving() {
return (
<div className="flex items-center gap-100">
<ViewSaveStatus status="saving" />
<SaveViewBar saving onDiscard={() => {}} onSave={() => {}} />
</div>
);
}Updating records
The same status line says when the grid reloads after a change.
import { ViewSaveStatus } from '@/ui/save-view-bar';
// The same line says when the grid reloads after a change.
export default function SaveViewBarUpdating() {
return <ViewSaveStatus status="updating" />;
}When a save can't finish
A save that can't confirm, or loses a race with someone else's, shows one warning line under the toolbar with the ways out. Nothing is discarded without the person choosing.
import { ViewSaveProblem } from '@/ui/save-view-bar';
export default function SaveViewBarProblems() {
return (
<div className="flex w-full flex-col gap-150">
<ViewSaveProblem kind="uncertain" onRetry={() => {}} />
<ViewSaveProblem kind="conflict" savedBy="Priya" viewName="Shortlisted" onUseShared={() => {}} onKeepMine={() => {}} />
</div>
);
}Period controls
The bar under a report's header that picks which numbers the page shows: a preset period, a typed range, and whose numbers. Every change reloads every figure on the page.
Table
The record grid: every list of Companies, Contacts, Jobs and Applications. 36px rows, a 40px header, hairlines both ways. Names in ink, everything else subtle.