Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/(main)/_context/user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ export function UserProvider({ children }: { children: React.ReactNode }) {
const isClient = use(IsClientContext);

return (
<UserContext
<UserContext.Provider
value={{
data,
isLoading: userIsLoading || !isClient,
isValidating: userIsValidating || !isClient,
}}
>
{children}
</UserContext>
</UserContext.Provider>
);
}
29 changes: 16 additions & 13 deletions app/(main)/instance/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function InstanceLayoutContent({ children }: { children: React.ReactNode }) {
<Alert variant="destructive">
<AlertTitle>Error</AlertTitle>
<AlertDescription>
{isError?.message || 'Instance not found.'}
Instance not found or failed to load.
</AlertDescription>
</Alert>
</div>
Expand Down Expand Up @@ -268,17 +268,11 @@ const TABS = [
{ value: 'configuration', label: 'Configuration', icon: Settings },
];

function InstanceTabs() {
const pathname = usePathname();
const { name: instanceName } = useInstanceContext();

// Determine current tab based on pathname
// /instance -> dashboard
// /instance/backups -> backups
// etc.
// More robust logic: extract tab from pathname, supporting only known tabs.
function getTabFromPathname(pathname: string): string {
// Default to dashboard
let currentTab = 'dashboard';
if (pathname.startsWith('/instance/')) {

if (pathname && pathname.startsWith('/instance/')) {
// Take the segment after /instance/
const segment = pathname.replace(/^\/instance\/?/, '').split('/')[0];
// Match only known TABS by value
Expand All @@ -287,14 +281,23 @@ function InstanceTabs() {
}
}

return currentTab;
}

function InstanceTabs() {
const pathname = usePathname();
const { name: instanceName } = useInstanceContext();

const currentTab = getTabFromPathname(pathname);

return (
<Tabs value={currentTab} className="w-full">
<div className="w-full overflow-x-auto">
<TabsList className="min-w-full inline-flex">
{TABS.map((tab) => {
const targetPath =
tab.value === 'dashboard' ? '/instance' : `/instance/${tab.value}`;

const Icon = tab.icon;
return (
<TabsTrigger key={tab.value} value={tab.value} asChild>
<Link
Expand All @@ -303,7 +306,7 @@ function InstanceTabs() {
query: instanceName ? { name: instanceName } : undefined,
}}
>
<tab.icon aria-hidden="true" className="mr-2 h-4 w-4" />
<Icon aria-hidden="true" className="mr-2 h-4 w-4" />
{tab.label}
</Link>
</TabsTrigger>
Expand Down
25 changes: 6 additions & 19 deletions app/(main)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default function DashboardLayout({
}: {
children: React.ReactNode;
}) {
const variant = 'inset';
return (
<UserProvider>
<SidebarProvider
Expand All @@ -20,24 +19,12 @@ export default function DashboardLayout({
} as React.CSSProperties
}
>
<AppSidebar variant={variant} />
{variant != 'inset' ? (
<>
<div className="w-full h-full">
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
{children}
</div>
</div>
</div>
</>
) : (
<SidebarInset>
<ProjectsProvider>
{children}
</ProjectsProvider>
</SidebarInset>
)}
<AppSidebar variant="inset" />
<SidebarInset>
<ProjectsProvider>
{children}
</ProjectsProvider>
</SidebarInset>
</SidebarProvider>
</UserProvider>
);
Expand Down
13 changes: 7 additions & 6 deletions app/_lib/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ function processConfigurableOptions(config: ConfigurableOptions) {
};

// Helper to process a single option
const MATCHED_TYPE_BOTH = 'both' as const;
const getOptionIdentifier = (option: ConfigOption) =>
option.name || option.key || '[unknown key]';

const processOption = (option: ConfigOption) => {
const textToCheck = [option.condition, option.shortdesc, option.longdesc]
Expand All @@ -55,25 +56,25 @@ function processConfigurableOptions(config: ConfigurableOptions) {

// Default to supporting both types
option.supported_types = ['container', 'virtual-machine'];
let typeMatchCategory: 'both' | 'container' | 'virtual-machine' = MATCHED_TYPE_BOTH;
let matchedType: 'both' | 'container' | 'virtual-machine' = 'both';
// Check for container-only patterns
if (
TYPE_PATTERNS.container.some((pattern) => textToCheck.includes(pattern))
) {
option.supported_types = ['container'];
typeMatchCategory = 'container';
matchedType = 'container';
}
// Check for VM-only patterns
else if (
TYPE_PATTERNS.vm.some((pattern) => textToCheck.includes(pattern))
) {
option.supported_types = ['virtual-machine'];
typeMatchCategory = 'virtual-machine';
matchedType = 'virtual-machine';
}
// Warn if no pattern matched and falling back to both
if (typeMatchCategory === MATCHED_TYPE_BOTH && process.env.NODE_ENV === 'development') {
if (matchedType === 'both' && process.env.NODE_ENV === 'development') {
console.warn(
`[processOption] Option ${option.name || option.key || '[unknown key]'} uses the default 'both' instance types due to unmatched pattern:`,
`[processOption] Option ${getOptionIdentifier(option)} uses the default 'both' instance types due to unmatched pattern:`,
textToCheck
);
}
Expand Down