feat(ui): company-prefix routes, archive company, hide archived from sidebar

Support optional company-prefix in URL paths (e.g. /PAP/issues/PAP-1).
Filter archived companies from sidebar rail, switcher, and auto-select.
Add archive button to company settings with confirmation dialog.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-03-02 10:31:54 -06:00
parent ce3b31d2c3
commit 410164a632
5 changed files with 134 additions and 50 deletions

View File

@@ -135,19 +135,26 @@ function SortableCompanyItem({
export function CompanyRail() {
const { companies, selectedCompanyId, setSelectedCompanyId } = useCompany();
const { openOnboarding } = useDialog();
const sidebarCompanies = useMemo(
() => companies.filter((company) => company.status !== "archived"),
[companies],
);
// Maintain sorted order in local state, synced from companies + localStorage
const [orderedIds, setOrderedIds] = useState<string[]>(() =>
sortByStoredOrder(companies).map((c) => c.id)
sortByStoredOrder(sidebarCompanies).map((c) => c.id)
);
// Re-sync orderedIds from localStorage whenever companies changes.
// Handles initial data load (companies starts as [] before query resolves)
// and subsequent refetches triggered by live updates.
useEffect(() => {
if (companies.length === 0) return;
setOrderedIds(sortByStoredOrder(companies).map((c) => c.id));
}, [companies]);
if (sidebarCompanies.length === 0) {
setOrderedIds([]);
return;
}
setOrderedIds(sortByStoredOrder(sidebarCompanies).map((c) => c.id));
}, [sidebarCompanies]);
// Sync order across tabs via the native storage event
useEffect(() => {
@@ -164,7 +171,7 @@ export function CompanyRail() {
// Re-derive when companies change (new company added/removed)
const orderedCompanies = useMemo(() => {
const byId = new Map(companies.map((c) => [c.id, c]));
const byId = new Map(sidebarCompanies.map((c) => [c.id, c]));
const result: Company[] = [];
for (const id of orderedIds) {
const c = byId.get(id);
@@ -178,7 +185,7 @@ export function CompanyRail() {
result.push(c);
}
return result;
}, [companies, orderedIds]);
}, [sidebarCompanies, orderedIds]);
// Require 8px of movement before starting a drag to avoid interfering with clicks
const sensors = useSensors(

View File

@@ -26,6 +26,7 @@ function statusDotColor(status?: string): string {
export function CompanySwitcher() {
const { companies, selectedCompany, setSelectedCompanyId } = useCompany();
const sidebarCompanies = companies.filter((company) => company.status !== "archived");
return (
<DropdownMenu>
@@ -48,7 +49,7 @@ export function CompanySwitcher() {
<DropdownMenuContent align="start" className="w-[220px]">
<DropdownMenuLabel>Companies</DropdownMenuLabel>
<DropdownMenuSeparator />
{companies.map((company) => (
{sidebarCompanies.map((company) => (
<DropdownMenuItem
key={company.id}
onClick={() => setSelectedCompanyId(company.id)}
@@ -58,7 +59,7 @@ export function CompanySwitcher() {
<span className="truncate">{company.name}</span>
</DropdownMenuItem>
))}
{companies.length === 0 && (
{sidebarCompanies.length === 0 && (
<DropdownMenuItem disabled>No companies</DropdownMenuItem>
)}
<DropdownMenuSeparator />