mirror of
https://github.com/fosrl/pangolin.git
synced 2026-07-06 12:19:50 +00:00
fix toolbar responsiveness and disable side panel
This commit is contained in:
@@ -271,6 +271,7 @@ export default async function migration() {
|
|||||||
`ALTER TABLE 'sites' DROP COLUMN 'remoteSubnets';`
|
`ALTER TABLE 'sites' DROP COLUMN 'remoteSubnets';`
|
||||||
).run();
|
).run();
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
// get all of the siteResources and set the aliasAddress to 100.96.128.x starting at .8
|
// get all of the siteResources and set the aliasAddress to 100.96.128.x starting at .8
|
||||||
const siteResourcesForAlias = db
|
const siteResourcesForAlias = db
|
||||||
.prepare(
|
.prepare(
|
||||||
@@ -278,6 +279,15 @@ export default async function migration() {
|
|||||||
)
|
)
|
||||||
.all() as {
|
.all() as {
|
||||||
siteResourceId: number;
|
siteResourceId: number;
|
||||||
|
=======
|
||||||
|
// Associate clients with site resources based on their previous site access
|
||||||
|
// Get all client-site associations from the renamed clientSitesAssociationsCache table
|
||||||
|
const clientSiteAssociations = db
|
||||||
|
.prepare(`SELECT clientId, siteId FROM 'clientSitesAssociationsCache'`)
|
||||||
|
.all() as {
|
||||||
|
clientId: number;
|
||||||
|
siteId: number;
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
const updateAliasAddress = db.prepare(
|
const updateAliasAddress = db.prepare(
|
||||||
@@ -335,6 +345,7 @@ export default async function migration() {
|
|||||||
`INSERT INTO 'clientSiteResources' ('clientId', 'siteResourceId') VALUES (?, ?)`
|
`INSERT INTO 'clientSiteResources' ('clientId', 'siteResourceId') VALUES (?, ?)`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
// create a clientSiteResourcesAssociationsCache entry for each existing association as well
|
// create a clientSiteResourcesAssociationsCache entry for each existing association as well
|
||||||
const insertClientSiteResourceCache = db.prepare(
|
const insertClientSiteResourceCache = db.prepare(
|
||||||
`INSERT INTO 'clientSiteResourcesAssociationsCache' ('clientId', 'siteResourceId') VALUES (?, ?)`
|
`INSERT INTO 'clientSiteResourcesAssociationsCache' ('clientId', 'siteResourceId') VALUES (?, ?)`
|
||||||
@@ -345,6 +356,13 @@ export default async function migration() {
|
|||||||
const siteResources = getSiteResources.all(
|
const siteResources = getSiteResources.all(
|
||||||
association.siteId
|
association.siteId
|
||||||
) as {
|
) as {
|
||||||
|
=======
|
||||||
|
// For each client-site association, find all site resources for that site
|
||||||
|
for (const association of clientSiteAssociations) {
|
||||||
|
const siteResources = db
|
||||||
|
.prepare(`SELECT siteResourceId FROM 'siteResources' WHERE siteId = ?`)
|
||||||
|
.all(association.siteId) as {
|
||||||
|
>>>>>>> Stashed changes
|
||||||
siteResourceId: number;
|
siteResourceId: number;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
@@ -352,15 +370,25 @@ export default async function migration() {
|
|||||||
for (const siteResource of siteResources) {
|
for (const siteResource of siteResources) {
|
||||||
insertClientSiteResource.run(
|
insertClientSiteResource.run(
|
||||||
association.clientId,
|
association.clientId,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
siteResource.siteResourceId
|
siteResource.siteResourceId
|
||||||
);
|
);
|
||||||
insertClientSiteResourceCache.run(
|
insertClientSiteResourceCache.run(
|
||||||
association.clientId,
|
association.clientId,
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
siteResource.siteResourceId
|
siteResource.siteResourceId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all clients for niceId population (used later)
|
||||||
|
const clients = db
|
||||||
|
.prepare(`SELECT clientId FROM 'clients'`)
|
||||||
|
.all() as {
|
||||||
|
clientId: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
// Associate existing site resources with their org's admin role
|
// Associate existing site resources with their org's admin role
|
||||||
const siteResourcesWithOrg = db
|
const siteResourcesWithOrg = db
|
||||||
.prepare(`SELECT siteResourceId, orgId FROM 'siteResources'`)
|
.prepare(`SELECT siteResourceId, orgId FROM 'siteResources'`)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type LauncherGroupListProps = {
|
|||||||
};
|
};
|
||||||
resourcesByGroupKey: Record<string, LauncherGroupResources>;
|
resourcesByGroupKey: Record<string, LauncherGroupResources>;
|
||||||
onClearFilters?: () => void;
|
onClearFilters?: () => void;
|
||||||
onResourceSelect: (resource: LauncherResource) => void;
|
onResourceSelect?: (resource: LauncherResource) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function hasActiveLauncherFilters(config: LauncherViewConfig): boolean {
|
function hasActiveLauncherFilters(config: LauncherViewConfig): boolean {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ type LauncherGroupSectionProps = {
|
|||||||
pageSize: number;
|
pageSize: number;
|
||||||
};
|
};
|
||||||
defaultOpen?: boolean;
|
defaultOpen?: boolean;
|
||||||
onResourceSelect: (resource: LauncherResource) => void;
|
onResourceSelect?: (resource: LauncherResource) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LauncherGroupSection({
|
export function LauncherGroupSection({
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { getLauncherResourceSelectProps } from "./useLauncherResourceAction";
|
|||||||
type LauncherResourceCardProps = {
|
type LauncherResourceCardProps = {
|
||||||
resource: LauncherResource;
|
resource: LauncherResource;
|
||||||
showLabels: boolean;
|
showLabels: boolean;
|
||||||
onSelect: () => void;
|
onSelect?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LauncherResourceCard({
|
export function LauncherResourceCard({
|
||||||
@@ -19,18 +19,20 @@ export function LauncherResourceCard({
|
|||||||
onSelect
|
onSelect
|
||||||
}: LauncherResourceCardProps) {
|
}: LauncherResourceCardProps) {
|
||||||
const hasIcon = Boolean(resource.iconUrl);
|
const hasIcon = Boolean(resource.iconUrl);
|
||||||
const clickProps = getLauncherResourceSelectProps(onSelect);
|
const clickProps = onSelect
|
||||||
|
? getLauncherResourceSelectProps(onSelect)
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex min-w-0 flex-col gap-2.5 overflow-hidden rounded-xl border border-border bg-background p-4",
|
"flex min-w-0 flex-col gap-2.5 overflow-hidden rounded-xl border border-border bg-background p-4",
|
||||||
clickProps.className
|
clickProps?.className
|
||||||
)}
|
)}
|
||||||
onClick={clickProps.onClick}
|
onClick={clickProps?.onClick}
|
||||||
onKeyDown={clickProps.onKeyDown}
|
onKeyDown={clickProps?.onKeyDown}
|
||||||
role={clickProps.role}
|
role={clickProps?.role}
|
||||||
tabIndex={clickProps.tabIndex}
|
tabIndex={clickProps?.tabIndex}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { LauncherResourceCard } from "./LauncherResourceCard";
|
|||||||
type LauncherResourceGridProps = {
|
type LauncherResourceGridProps = {
|
||||||
resources: LauncherResource[];
|
resources: LauncherResource[];
|
||||||
showLabels: boolean;
|
showLabels: boolean;
|
||||||
onResourceSelect: (resource: LauncherResource) => void;
|
onResourceSelect?: (resource: LauncherResource) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LauncherResourceGrid({
|
export function LauncherResourceGrid({
|
||||||
@@ -21,7 +21,11 @@ export function LauncherResourceGrid({
|
|||||||
key={resource.launcherResourceKey}
|
key={resource.launcherResourceKey}
|
||||||
resource={resource}
|
resource={resource}
|
||||||
showLabels={showLabels}
|
showLabels={showLabels}
|
||||||
onSelect={() => onResourceSelect(resource)}
|
onSelect={
|
||||||
|
onResourceSelect
|
||||||
|
? () => onResourceSelect(resource)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { LauncherResourceRow } from "./LauncherResourceRow";
|
|||||||
type LauncherResourceListProps = {
|
type LauncherResourceListProps = {
|
||||||
resources: LauncherResource[];
|
resources: LauncherResource[];
|
||||||
showLabels: boolean;
|
showLabels: boolean;
|
||||||
onResourceSelect: (resource: LauncherResource) => void;
|
onResourceSelect?: (resource: LauncherResource) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LauncherResourceList({
|
export function LauncherResourceList({
|
||||||
@@ -23,7 +23,11 @@ export function LauncherResourceList({
|
|||||||
resource={resource}
|
resource={resource}
|
||||||
showLabels={showLabels}
|
showLabels={showLabels}
|
||||||
isLast={index === resources.length - 1}
|
isLast={index === resources.length - 1}
|
||||||
onSelect={() => onResourceSelect(resource)}
|
onSelect={
|
||||||
|
onResourceSelect
|
||||||
|
? () => onResourceSelect(resource)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type LauncherResourceRowProps = {
|
|||||||
resource: LauncherResource;
|
resource: LauncherResource;
|
||||||
showLabels: boolean;
|
showLabels: boolean;
|
||||||
isLast?: boolean;
|
isLast?: boolean;
|
||||||
onSelect: () => void;
|
onSelect?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function LauncherResourceRow({
|
export function LauncherResourceRow({
|
||||||
@@ -21,19 +21,21 @@ export function LauncherResourceRow({
|
|||||||
onSelect
|
onSelect
|
||||||
}: LauncherResourceRowProps) {
|
}: LauncherResourceRowProps) {
|
||||||
const hasTags = showLabels && resource.labels.length > 0;
|
const hasTags = showLabels && resource.labels.length > 0;
|
||||||
const clickProps = getLauncherResourceSelectProps(onSelect);
|
const clickProps = onSelect
|
||||||
|
? getLauncherResourceSelectProps(onSelect)
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2.5 p-4 max-md:min-w-max max-md:whitespace-nowrap",
|
"flex items-center gap-2.5 p-4 max-md:min-w-max max-md:whitespace-nowrap",
|
||||||
isLast ? undefined : "border-b border-border",
|
isLast ? undefined : "border-b border-border",
|
||||||
clickProps.className
|
clickProps?.className
|
||||||
)}
|
)}
|
||||||
onClick={clickProps.onClick}
|
onClick={clickProps?.onClick}
|
||||||
onKeyDown={clickProps.onKeyDown}
|
onKeyDown={clickProps?.onKeyDown}
|
||||||
role={clickProps.role}
|
role={clickProps?.role}
|
||||||
tabIndex={clickProps.tabIndex}
|
tabIndex={clickProps?.tabIndex}
|
||||||
>
|
>
|
||||||
<LauncherResourceIcon
|
<LauncherResourceIcon
|
||||||
iconUrl={resource.iconUrl}
|
iconUrl={resource.iconUrl}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export function LauncherViewTabs({
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 overflow-x-auto max-w-full shrink min-w-0">
|
<div className="flex w-max items-center gap-2">
|
||||||
{viewOptions.map((option) => {
|
{viewOptions.map((option) => {
|
||||||
const isSelected = activeViewId === option.value;
|
const isSelected = activeViewId === option.value;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import { useToast } from "@app/hooks/useToast";
|
|||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import type {
|
import type {
|
||||||
LauncherGroup,
|
LauncherGroup,
|
||||||
LauncherResource,
|
|
||||||
LauncherViewConfig,
|
LauncherViewConfig,
|
||||||
LauncherViewRecord
|
LauncherViewRecord
|
||||||
} from "@server/routers/launcher/types";
|
} from "@server/routers/launcher/types";
|
||||||
@@ -51,12 +50,13 @@ import {
|
|||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import type { Selectedsite } from "@app/components/site-selector";
|
import type { Selectedsite } from "@app/components/site-selector";
|
||||||
import type { SelectedLabel } from "@app/components/labels-selector";
|
import type { SelectedLabel } from "@app/components/labels-selector";
|
||||||
|
import { useMediaQuery } from "@app/hooks/useMediaQuery";
|
||||||
|
import { cn } from "@app/lib/cn";
|
||||||
import { LauncherFilterPopover } from "./LauncherFilterPopover";
|
import { LauncherFilterPopover } from "./LauncherFilterPopover";
|
||||||
import { LauncherGroupList } from "./LauncherGroupList";
|
import { LauncherGroupList } from "./LauncherGroupList";
|
||||||
import { LauncherRefreshButton } from "./LauncherRefreshButton";
|
import { LauncherRefreshButton } from "./LauncherRefreshButton";
|
||||||
import { LauncherSettingsMenu } from "./LauncherSettingsMenu";
|
import { LauncherSettingsMenu } from "./LauncherSettingsMenu";
|
||||||
import { LauncherSortButton } from "./LauncherSortButton";
|
import { LauncherSortButton } from "./LauncherSortButton";
|
||||||
import { LauncherResourcePanel } from "./LauncherResourcePanel";
|
|
||||||
import { LauncherSaveViewMenu, LauncherViewTabs } from "./LauncherViewTabs";
|
import { LauncherSaveViewMenu, LauncherViewTabs } from "./LauncherViewTabs";
|
||||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||||
|
|
||||||
@@ -98,11 +98,11 @@ export default function ResourceLauncher({
|
|||||||
|
|
||||||
const [searchInputResetKey, setSearchInputResetKey] = useState(0);
|
const [searchInputResetKey, setSearchInputResetKey] = useState(0);
|
||||||
const [saveDialogOpen, setSaveDialogOpen] = useState(false);
|
const [saveDialogOpen, setSaveDialogOpen] = useState(false);
|
||||||
const [selectedResource, setSelectedResource] =
|
|
||||||
useState<LauncherResource | null>(null);
|
|
||||||
const [newViewName, setNewViewName] = useState("");
|
const [newViewName, setNewViewName] = useState("");
|
||||||
const [saveOrgWide, setSaveOrgWide] = useState(false);
|
const [saveOrgWide, setSaveOrgWide] = useState(false);
|
||||||
|
|
||||||
|
const isDesktop = useMediaQuery("(min-width: 768px)");
|
||||||
|
|
||||||
const configRef = useRef(config);
|
const configRef = useRef(config);
|
||||||
configRef.current = config;
|
configRef.current = config;
|
||||||
const searchInputRef = useRef(config.query);
|
const searchInputRef = useRef(config.query);
|
||||||
@@ -392,6 +392,90 @@ export default function ResourceLauncher({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const savedViewTabs = views.map((view) => ({
|
||||||
|
viewId: view.viewId,
|
||||||
|
name: view.name
|
||||||
|
}));
|
||||||
|
|
||||||
|
const renderToolbarSearch = (searchClassName: string) => (
|
||||||
|
<div className={cn("relative shrink-0", searchClassName)}>
|
||||||
|
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
|
||||||
|
<Input
|
||||||
|
key={`${activeViewId}-${searchInputResetKey}`}
|
||||||
|
defaultValue={config.query}
|
||||||
|
onChange={(event) => {
|
||||||
|
const value = event.currentTarget.value;
|
||||||
|
searchInputRef.current = value;
|
||||||
|
debouncedNavigateSearch(activeViewIdRef.current, value);
|
||||||
|
}}
|
||||||
|
placeholder={t("resourceLauncherSearchPlaceholder")}
|
||||||
|
className="pl-8"
|
||||||
|
type="search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderToolbarActions = () => (
|
||||||
|
<>
|
||||||
|
<LauncherSaveViewMenu
|
||||||
|
isDefaultView={isDefaultView}
|
||||||
|
isAdmin={isAdmin}
|
||||||
|
isOrgWideView={isOrgWideView}
|
||||||
|
hasUnsavedChanges={hasUnsavedChanges}
|
||||||
|
onSaveToCurrent={handleSaveToCurrent}
|
||||||
|
onSaveAsNew={handleSaveAsNew}
|
||||||
|
onSaveForEveryone={handleSaveForEveryone}
|
||||||
|
onMakePersonal={handleMakePersonal}
|
||||||
|
onResetView={handleResetView}
|
||||||
|
/>
|
||||||
|
<LauncherFilterPopover
|
||||||
|
orgId={orgId}
|
||||||
|
selectedSites={selectedSites}
|
||||||
|
selectedLabels={selectedLabels}
|
||||||
|
onSitesChange={(sites) =>
|
||||||
|
applyConfigPatch({
|
||||||
|
siteIds: sites.map((site) => site.siteId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onLabelsChange={(labels) =>
|
||||||
|
applyConfigPatch({
|
||||||
|
labelIds: labels.map((label) => label.labelId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<LauncherSortButton
|
||||||
|
order={config.order}
|
||||||
|
onToggle={() =>
|
||||||
|
applyConfigPatch({
|
||||||
|
order: config.order === "asc" ? "desc" : "asc"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<LauncherSettingsMenu
|
||||||
|
config={config}
|
||||||
|
isDefaultView={isDefaultView}
|
||||||
|
onConfigChange={applyConfigPatch}
|
||||||
|
onDeleteView={() => {
|
||||||
|
if (!isDefaultView) {
|
||||||
|
deleteViewMutation.mutate(activeViewId);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<LauncherRefreshButton
|
||||||
|
onRefresh={refreshData}
|
||||||
|
isRefreshing={isRefreshing || isNavigating}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderToolbarViews = () => (
|
||||||
|
<LauncherViewTabs
|
||||||
|
activeViewId={activeViewId}
|
||||||
|
savedViews={savedViewTabs}
|
||||||
|
onSelectView={selectView}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col" aria-busy={isNavigating}>
|
<div className="flex flex-col" aria-busy={isNavigating}>
|
||||||
<SettingsSectionTitle
|
<SettingsSectionTitle
|
||||||
@@ -399,93 +483,27 @@ export default function ResourceLauncher({
|
|||||||
description={t("resourceLauncherDescription")}
|
description={t("resourceLauncherDescription")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 mb-6">
|
{isDesktop ? (
|
||||||
<div className="flex flex-row items-center gap-3 justify-between max-md:flex-col max-md:items-stretch">
|
<div className="mb-6 flex w-full min-w-0 items-center gap-3">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center gap-3 min-w-0 flex-1 max-md:order-2">
|
{renderToolbarSearch("w-64")}
|
||||||
<div className="relative w-full sm:max-w-sm shrink-0">
|
<div className="min-w-0 flex-1 overflow-x-auto">
|
||||||
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
|
{renderToolbarViews()}
|
||||||
<Input
|
|
||||||
key={`${activeViewId}-${searchInputResetKey}`}
|
|
||||||
defaultValue={config.query}
|
|
||||||
onChange={(event) => {
|
|
||||||
const value = event.currentTarget.value;
|
|
||||||
searchInputRef.current = value;
|
|
||||||
debouncedNavigateSearch(
|
|
||||||
activeViewIdRef.current,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
placeholder={t(
|
|
||||||
"resourceLauncherSearchPlaceholder"
|
|
||||||
)}
|
|
||||||
className="pl-8"
|
|
||||||
type="search"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<LauncherViewTabs
|
|
||||||
activeViewId={activeViewId}
|
|
||||||
savedViews={views.map((view) => ({
|
|
||||||
viewId: view.viewId,
|
|
||||||
name: view.name
|
|
||||||
}))}
|
|
||||||
onSelectView={selectView}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 shrink-0 justify-end max-md:order-1 max-md:justify-start">
|
<div className="flex shrink-0 items-center gap-2">
|
||||||
<LauncherSaveViewMenu
|
{renderToolbarActions()}
|
||||||
isDefaultView={isDefaultView}
|
|
||||||
isAdmin={isAdmin}
|
|
||||||
isOrgWideView={isOrgWideView}
|
|
||||||
hasUnsavedChanges={hasUnsavedChanges}
|
|
||||||
onSaveToCurrent={handleSaveToCurrent}
|
|
||||||
onSaveAsNew={handleSaveAsNew}
|
|
||||||
onSaveForEveryone={handleSaveForEveryone}
|
|
||||||
onMakePersonal={handleMakePersonal}
|
|
||||||
onResetView={handleResetView}
|
|
||||||
/>
|
|
||||||
<LauncherFilterPopover
|
|
||||||
orgId={orgId}
|
|
||||||
selectedSites={selectedSites}
|
|
||||||
selectedLabels={selectedLabels}
|
|
||||||
onSitesChange={(sites) =>
|
|
||||||
applyConfigPatch({
|
|
||||||
siteIds: sites.map((site) => site.siteId)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onLabelsChange={(labels) =>
|
|
||||||
applyConfigPatch({
|
|
||||||
labelIds: labels.map(
|
|
||||||
(label) => label.labelId
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<LauncherSortButton
|
|
||||||
order={config.order}
|
|
||||||
onToggle={() =>
|
|
||||||
applyConfigPatch({
|
|
||||||
order:
|
|
||||||
config.order === "asc" ? "desc" : "asc"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<LauncherSettingsMenu
|
|
||||||
config={config}
|
|
||||||
isDefaultView={isDefaultView}
|
|
||||||
onConfigChange={applyConfigPatch}
|
|
||||||
onDeleteView={() => {
|
|
||||||
if (!isDefaultView) {
|
|
||||||
deleteViewMutation.mutate(activeViewId);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<LauncherRefreshButton
|
|
||||||
onRefresh={refreshData}
|
|
||||||
isRefreshing={isRefreshing || isNavigating}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
<div className="mb-6 flex flex-col gap-3">
|
||||||
|
<div className="flex items-center gap-2 overflow-x-auto">
|
||||||
|
{renderToolbarActions()}
|
||||||
|
</div>
|
||||||
|
{renderToolbarSearch("w-full")}
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
{renderToolbarViews()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<LauncherGroupList
|
<LauncherGroupList
|
||||||
orgId={orgId}
|
orgId={orgId}
|
||||||
@@ -495,19 +513,6 @@ export default function ResourceLauncher({
|
|||||||
groupsPagination={groupsPagination}
|
groupsPagination={groupsPagination}
|
||||||
resourcesByGroupKey={resourcesByGroupKey}
|
resourcesByGroupKey={resourcesByGroupKey}
|
||||||
onClearFilters={handleClearFilters}
|
onClearFilters={handleClearFilters}
|
||||||
onResourceSelect={setSelectedResource}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<LauncherResourcePanel
|
|
||||||
open={selectedResource != null}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setSelectedResource(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
resource={selectedResource}
|
|
||||||
orgId={orgId}
|
|
||||||
isAdmin={isAdmin}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Credenza open={saveDialogOpen} onOpenChange={setSaveDialogOpen}>
|
<Credenza open={saveDialogOpen} onOpenChange={setSaveDialogOpen}>
|
||||||
|
|||||||
Reference in New Issue
Block a user