| 1 | import { Button } from "../ui/button"; |
| 2 | import { Alert, AlertAction, AlertDescription } from "../ui/alert"; |
| 3 | import { SettingsPageContainer } from "./settingsLayout"; |
| 4 | import { useSettingsScope } from "./SettingsScopeContext"; |
| 5 | import { useEnvironments } from "../../state/environments"; |
| 6 | import type { SettingsScopeSearch } from "./settingsScope"; |
| 7 | import { useSettingsProjectGroups } from "./useSettingsProjectGroups"; |
| 8 | import { useLocation, useNavigate } from "@tanstack/react-router"; |
| 9 | import type { EnvironmentId } from "@t3tools/contracts"; |
| 10 | |
| 11 | /** Offer an explicit target change when a category has no settings at this scope. */ |
| 12 | export function SettingsScopeNotice({ |
| 13 | children, |
| 14 | target, |
| 15 | targetId, |
| 16 | eligibleEnvironmentIds, |
| 17 | }: { |
| 18 | children: string; |
| 19 | target: "environment" | "all" | "project" | "checkout"; |
| 20 | targetId?: string; |
| 21 | eligibleEnvironmentIds?: readonly EnvironmentId[]; |
| 22 | }) { |
| 23 | const { selectScope, search } = useSettingsScope(); |
| 24 | const navigate = useNavigate({ from: "/settings" }); |
| 25 | const pathname = useLocation({ select: (location) => location.pathname }); |
| 26 | const { environments } = useEnvironments(); |
| 27 | const groups = useSettingsProjectGroups(); |
| 28 | const choices: { label: string; search: SettingsScopeSearch }[] = |
| 29 | target === "checkout" |
| 30 | ? groups |
| 31 | .filter((group) => !search.project || group.projectKey === search.project) |
| 32 | .flatMap((group) => |
| 33 | group.memberProjects.map((member) => ({ |
| 34 | label: `${group.displayName} · ${member.environmentLabel ?? "Environment"} · ${member.workspaceRoot}`, |
| 35 | search: { |
| 36 | project: group.projectKey, |
| 37 | machine: member.environmentId, |
| 38 | checkout: member.physicalProjectKey, |
| 39 | }, |
| 40 | })), |
| 41 | ) |
| 42 | : target === "project" |
| 43 | ? groups.map((group) => ({ |
| 44 | label: group.displayName, |
| 45 | search: { project: group.projectKey }, |
| 46 | })) |
| 47 | : target === "environment" |
| 48 | ? environments |
| 49 | .filter( |
| 50 | (entry) => |
| 51 | eligibleEnvironmentIds === undefined || |
| 52 | eligibleEnvironmentIds.includes(entry.environmentId), |
| 53 | ) |
| 54 | .map((entry) => ({ |
| 55 | label: environments.some( |
| 56 | (other) => |
| 57 | other.environmentId !== entry.environmentId && other.label === entry.label, |
| 58 | ) |
| 59 | ? `${entry.label} · ${entry.displayUrl || entry.environmentId}` |
| 60 | : entry.label, |
| 61 | search: { machine: entry.environmentId }, |
| 62 | })) |
| 63 | : [{ label: "Open all environments", search: {} }]; |
| 64 | return ( |
| 65 | <SettingsPageContainer> |
| 66 | <Alert role="status"> |
| 67 | <AlertDescription> |
| 68 | <p>{children}</p> |
| 69 | <AlertAction className="flex-wrap"> |
| 70 | {choices.map((choice) => ( |
| 71 | <Button |
| 72 | key={JSON.stringify(choice.search)} |
| 73 | size="sm-multiline" |
| 74 | variant="outline" |
| 75 | className="max-w-full break-all text-left" |
| 76 | onClick={() => { |
| 77 | if (targetId) |
| 78 | void navigate({ to: pathname, search: () => choice.search, hash: targetId }); |
| 79 | else selectScope(choice.search); |
| 80 | }} |
| 81 | > |
| 82 | {choice.label} |
| 83 | </Button> |
| 84 | ))} |
| 85 | </AlertAction> |
| 86 | </AlertDescription> |
| 87 | </Alert> |
| 88 | </SettingsPageContainer> |
| 89 | ); |
| 90 | } |