Merge remote-tracking branch 'origin/dev' into feat/ab/sh-281-company-directory

This commit is contained in:
Codex Review Integration 2026-09-15 14:07:08 -03:00
commit c8c7533ee7
11 changed files with 94 additions and 32 deletions

View file

@ -83,11 +83,12 @@ jobs:
echo "prefix=${prefix}"
} >> "${GITHUB_OUTPUT}"
# Sentry release is shoc-frontend@${GITHUB_SHA} via VITE_APP_COMMIT_SHA,
# distinct from the S3/Terraform version_label.
- name: Upload private source maps
run: bash scripts/upload-sourcemaps.sh
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_RELEASE: ${{ steps.release.outputs.version_label }}
- name: Read previous release pointer
id: pointer

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 144 KiB

View file

@ -6,18 +6,13 @@ set -euo pipefail
SENTRY_ORG="${SENTRY_ORG:-seahaven}"
SENTRY_PROJECT="${SENTRY_PROJECT:-shoc-frontend}"
COMMIT_SHA="${VITE_APP_COMMIT_SHA:-${GITHUB_SHA:-}}"
RELEASE_LABEL="${SENTRY_RELEASE:-${RELEASE_LABEL:-}}"
if [[ -n "${RELEASE_LABEL}" ]]; then
RELEASE="${RELEASE_LABEL}"
else
if [[ ! "${COMMIT_SHA}" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "::error::Source-map upload requires a 40-character VITE_APP_COMMIT_SHA or GITHUB_SHA." >&2
exit 1
fi
COMMIT_SHA="$(printf '%s' "${COMMIT_SHA}" | tr '[:upper:]' '[:lower:]')"
RELEASE="shoc-frontend@${COMMIT_SHA}"
if [[ ! "${COMMIT_SHA}" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "::error::Source-map upload requires a 40-character VITE_APP_COMMIT_SHA or GITHUB_SHA." >&2
exit 1
fi
COMMIT_SHA="$(printf '%s' "${COMMIT_SHA}" | tr '[:upper:]' '[:lower:]')"
RELEASE="shoc-frontend@${COMMIT_SHA}"
npm exec --no -- sentry-cli sourcemaps upload \
--org "${SENTRY_ORG}" \

View file

@ -13,11 +13,7 @@ import {
import { AdvDocPill, AdvStatusPill } from "./adv-filter-pill";
import { AdvDateRangeSection } from "./adv-date-range-section";
import { AdvMultiSelect } from "./adv-multi-select";
import {
AdvAvetaSection,
AdvFlagInternalUpliftSections,
AdvIndicatorsSection,
} from "./adv-parity-filter-sections";
import { AdvFlagInternalUpliftSections, AdvIndicatorsSection } from "./adv-parity-filter-sections";
import { AdvSection } from "./adv-section";
import { AdvSeverityList } from "./adv-severity-list";
import { AdvTypeGrid } from "./adv-type-grid";
@ -131,8 +127,6 @@ export function AdvancedSearchFilterSections({
<AdvIndicatorsSection draft={draft} setDraft={setDraft} />
<AdvAvetaSection draft={draft} setDraft={setDraft} />
<AdvSection icon={Flame} label="Service" count={draft.pmTypes.size}>
<AdvMultiSelect
options={pmTypes.map((pmType) => ({ value: pmType, label: pmType }))}

View file

@ -125,6 +125,12 @@ export function AdvIndicatorsSection({ draft, setDraft }: DraftProps) {
);
}
const FILTER_TOGGLE_CHECKBOX_SX = {
padding: 0,
color: "var(--color-border-hover)",
"&.Mui-checked": { color: "var(--color-primary)" },
} as const;
export function AdvAvetaSection({ draft, setDraft }: DraftProps) {
return (
<AdvSection icon={ShieldCheck} label="Aveta Compliance" count={draft.avetaOnly ? 1 : 0}>
@ -133,11 +139,7 @@ export function AdvAvetaSection({ draft, setDraft }: DraftProps) {
size="small"
checked={draft.avetaOnly}
onChange={() => setDraft((prev) => ({ ...prev, avetaOnly: !prev.avetaOnly }))}
sx={{
padding: 0,
color: "var(--color-border-hover)",
"&.Mui-checked": { color: "var(--warning)" },
}}
sx={FILTER_TOGGLE_CHECKBOX_SX}
/>
<span className="text-sm text-muted-foreground">Aveta required only</span>
</label>
@ -195,6 +197,8 @@ export function AdvFlagInternalUpliftSections({
</button>
</AdvSection>
<AdvAvetaSection draft={draft} setDraft={setDraft} />
<AdvSection icon={DollarSign} label="Uplift" count={draft.hasUplift ? 1 : 0}>
<label className="flex cursor-pointer items-center gap-1.5 select-none">
<Checkbox
@ -207,11 +211,7 @@ export function AdvFlagInternalUpliftSections({
upliftStatuses: prev.hasUplift ? new Set() : prev.upliftStatuses,
}))
}
sx={{
padding: 0,
color: "var(--color-border-hover)",
"&.Mui-checked": { color: "var(--color-primary)" },
}}
sx={FILTER_TOGGLE_CHECKBOX_SX}
/>
<span className="text-sm text-muted-foreground">Has uplift</span>
</label>

View file

@ -55,11 +55,17 @@ export function useAdvancedSearchDraft({
};
const handleClearAll = () => {
if (activeCount === 0) {
return;
}
setDraft(cloneAdvancedFilters(EMPTY_ADVANCED_FILTERS));
onClearAll();
};
const handleApply = () => {
if (activeCount === 0) {
return;
}
onApply(cloneAdvancedFilters(draft));
onOpenChange(false);
};

View file

@ -64,7 +64,12 @@ export function WorkOrdersAdvancedSearchSheet({
/>
<div className="flex shrink-0 items-center gap-2 border-t border-border px-4 py-3">
<Button variant="ghost" className="text-xs" onClick={handleClearAll}>
<Button
variant="ghost"
className="text-xs"
disabled={activeCount === 0}
onClick={handleClearAll}
>
Clear all
</Button>
<div className="flex flex-1 justify-center">
@ -74,7 +79,7 @@ export function WorkOrdersAdvancedSearchSheet({
</span>
)}
</div>
<Button className="px-5 text-xs" onClick={handleApply}>
<Button className="px-5 text-xs" disabled={activeCount === 0} onClick={handleApply}>
Apply filters
</Button>
</div>

View file

@ -33,7 +33,9 @@ export function CollapsibleSection({
>
<div className="flex items-center gap-2">
{Icon != null && <Icon size={16} className="text-primary" />}
<span className="text-sm font-semibold text-foreground">{label}</span>
<span className="text-[length:var(--text-base)] font-semibold text-foreground">
{label}
</span>
</div>
<div className="flex items-center gap-2">
<CountBadge count={count} />

View file

@ -152,6 +152,8 @@ describe("WorkOrders Advanced Search page wiring (SH-121)", () => {
expect(within(sheet).getByText("HVAC")).toBeInTheDocument();
expect(within(sheet).getByText("Vendor Co")).toBeInTheDocument();
fireEvent.click(within(sheet).getByRole("button", { name: /Aveta Compliance/i }));
fireEvent.click(within(sheet).getByRole("checkbox", { name: /Aveta required only/i }));
fireEvent.click(within(sheet).getByRole("button", { name: /apply filters/i }));
expect(applyAdvancedFilters).toHaveBeenCalled();
expect(setAdvOpen).toHaveBeenCalledWith(false);
@ -341,6 +343,45 @@ describe("WorkOrders Advanced Search page wiring (SH-121)", () => {
expect(screen.getByRole("button", { name: /work order types, all types/i })).toBeEnabled();
});
it("places Aveta Compliance immediately above Uplift", () => {
const view = renderWithProviders(<WorkOrdersListPage />, {
route: "/workorders",
withAuth: false,
});
fireEvent.click(screen.getByRole("button", { name: "Advanced Filters" }));
advOpen = true;
view.rerender(<WorkOrdersListPage />);
const sheet = screen.getByRole("dialog", { name: "Advanced Filters" });
const aveta = within(sheet).getByRole("button", { name: /Aveta Compliance/i });
const uplift = within(sheet).getByRole("button", { name: /^Uplift/i });
expect(aveta.closest(".border-b")?.nextElementSibling).toBe(uplift.closest(".border-b"));
});
it("keeps Clear all and Apply filters disabled until a filter is selected", () => {
const view = renderWithProviders(<WorkOrdersListPage />, {
route: "/workorders",
withAuth: false,
});
fireEvent.click(screen.getByRole("button", { name: "Advanced Filters" }));
advOpen = true;
view.rerender(<WorkOrdersListPage />);
const sheet = screen.getByRole("dialog", { name: "Advanced Filters" });
const clearAll = within(sheet).getByRole("button", { name: /clear all/i });
const apply = within(sheet).getByRole("button", { name: /apply filters/i });
expect(clearAll).toBeDisabled();
expect(apply).toBeDisabled();
fireEvent.click(within(sheet).getByRole("button", { name: /Aveta Compliance/i }));
fireEvent.click(within(sheet).getByRole("checkbox", { name: /Aveta required only/i }));
expect(clearAll).toBeEnabled();
expect(apply).toBeEnabled();
});
it("applies Aveta required only through Advanced Filters", () => {
const view = renderWithProviders(<WorkOrdersListPage />, {
route: "/workorders",

View file

@ -211,7 +211,9 @@ describe("WorkOrdersListHeader slice affordances", () => {
);
expect(screen.getByRole("button", { name: /new wo/i })).toBeInTheDocument();
expect(screen.getByLabelText("Advanced Filters")).toBeInTheDocument();
const filters = screen.getByRole("button", { name: "Advanced Filters" });
expect(filters).toBeInTheDocument();
expect(filters).toHaveTextContent("Filters");
});
});

View file

@ -0,0 +1,16 @@
import { screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { CollapsibleSection } from "@/components/ui/collapsible-section";
import { renderWithProviders } from "@/test/test-utils";
describe("CollapsibleSection", () => {
it("renders the section title at the 14px body size token", () => {
renderWithProviders(<CollapsibleSection label="Aveta Compliance">child</CollapsibleSection>, {
withAuth: false,
});
const title = screen.getByRole("button", { name: /Aveta Compliance/i }).querySelector("span");
expect(title).toHaveClass("text-[length:var(--text-base)]");
});
});