feat: update ui

This commit is contained in:
2024-11-08 18:53:30 +00:00
parent 887cb64878
commit 334d90e691
32 changed files with 829 additions and 262 deletions
+26
View File
@@ -0,0 +1,26 @@
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { Drawer } from "expo-router/drawer";
import React from "react";
import { useMedia } from "tamagui";
export default function Layout() {
const media = useMedia();
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Drawer
screenOptions={{
drawerType: media.sm ? "front" : "permanent",
drawerStyle: { width: 250 },
headerLeft: media.sm ? undefined : () => null,
}}
>
<Drawer.Screen name="hosts" options={{ title: "Hosts" }} />
<Drawer.Screen
name="terminal"
options={{ title: "Terminal", headerShown: true }}
/>
</Drawer>
</GestureHandlerRootView>
);
}
+3
View File
@@ -0,0 +1,3 @@
import HostsPage from "@/pages/hosts/page";
export default HostsPage;
+3
View File
@@ -0,0 +1,3 @@
import TerminalPage from "@/pages/terminal/page";
export default TerminalPage;
+6 -1
View File
@@ -27,7 +27,12 @@ export default function RootLayout() {
return (
<Providers>
<Stack>
<Stack.Screen name="+not-found" />
<Stack.Screen
name="index"
options={{ headerShown: false, title: "Loading..." }}
/>
<Stack.Screen name="(drawer)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" options={{ title: "Not Found" }} />
</Stack>
<StatusBar style="auto" />
</Providers>
+35 -10
View File
@@ -1,4 +1,4 @@
import React, { PropsWithChildren, useMemo, useState } from "react";
import React, { PropsWithChildren, useEffect, useMemo, useState } from "react";
import tamaguiConfig from "@/tamagui.config";
import {
DarkTheme,
@@ -8,6 +8,8 @@ import {
import { TamaguiProvider, Theme } from "@tamagui/core";
import useThemeStore from "@/stores/theme";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { router, usePathname, useRootNavigationState } from "expo-router";
import { useAuthStore } from "@/stores/auth";
type Props = PropsWithChildren;
@@ -33,16 +35,39 @@ const Providers = ({ children }: Props) => {
}, [theme, colorScheme]);
return (
<ThemeProvider value={navTheme}>
<TamaguiProvider config={tamaguiConfig} defaultTheme={colorScheme}>
<Theme name="blue">
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</Theme>
</TamaguiProvider>
</ThemeProvider>
<>
<AuthProvider />
<ThemeProvider value={navTheme}>
<TamaguiProvider config={tamaguiConfig} defaultTheme={colorScheme}>
<Theme name="blue">
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</Theme>
</TamaguiProvider>
</ThemeProvider>
</>
);
};
const AuthProvider = () => {
const pathname = usePathname();
const rootNavigationState = useRootNavigationState();
const { isLoggedIn } = useAuthStore();
useEffect(() => {
if (!rootNavigationState?.key) {
return;
}
if (!pathname.startsWith("/auth") && !isLoggedIn) {
router.replace("/auth/login");
} else if (pathname.startsWith("/auth") && isLoggedIn) {
router.replace("/");
}
}, [pathname, rootNavigationState, isLoggedIn]);
return null;
};
export default Providers;
+14
View File
@@ -0,0 +1,14 @@
import { View, Text, Button } from "tamagui";
import React from "react";
import authStore from "@/stores/auth";
export default function LoginPage() {
return (
<View>
<Text>LoginPage</Text>
<Button onPress={() => authStore.setState({ token: "123" })}>
Login
</Button>
</View>
);
}
-69
View File
@@ -1,69 +0,0 @@
import Icons from "@/components/ui/icons";
import Select, { SelectItem } from "@/components/ui/select";
import api from "@/lib/api";
import { useQuery } from "@tanstack/react-query";
import React from "react";
import { Button, Input, Label, ScrollView, Text, View, XStack } from "tamagui";
type Props = {};
const typeOptions: SelectItem[] = [
{ label: "SSH", value: "ssh" },
{ label: "Proxmox VE", value: "pve" },
{ label: "Incus", value: "incus" },
];
const HostForm = (props: Props) => {
const keys = useQuery({
queryKey: ["keychains"],
queryFn: () => api("/keychains"),
select: (i) => i.rows,
});
return (
<>
<ScrollView contentContainerStyle={{ padding: "$4" }}>
<Label>Hostname</Label>
<Input placeholder="IP or hostname..." />
<Label>Type</Label>
<Select items={typeOptions} />
<Label>Port</Label>
<Input keyboardType="number-pad" placeholder="SSH Port" />
<Label>Label</Label>
<Input placeholder="Label..." />
<XStack gap="$3" mt="$3" mb="$1">
<Label flex={1}>Credentials</Label>
<Button size="$3" icon={<Icons size={16} name="plus" />}>
Add
</Button>
</XStack>
<Select
placeholder="Username & Password"
items={keys.data?.map((key: any) => ({
label: key.label,
value: key.id,
}))}
/>
<Select
mt="$3"
placeholder="Private Key"
items={keys.data?.map((key: any) => ({
label: key.label,
value: key.id,
}))}
/>
</ScrollView>
<View p="$4">
<Button icon={<Icons name="content-save" size={18} />}>Save</Button>
</View>
</>
);
};
export default HostForm;
+1 -1
View File
@@ -1,6 +1,6 @@
import React from "react";
import { Stack } from "expo-router";
import HostForm from "./_comp/host-form";
import HostForm from "@/pages/hosts/components/form";
export default function CreateHostPage() {
return (
+1 -1
View File
@@ -1,6 +1,6 @@
import React from "react";
import { Stack } from "expo-router";
import HostForm from "./_comp/host-form";
import HostForm from "@/pages/hosts/components/form";
export default function EditHostPage() {
return (
-80
View File
@@ -1,80 +0,0 @@
import { View, Text, Button, ScrollView, Spinner, Card, XStack } from "tamagui";
import React from "react";
import useThemeStore from "@/stores/theme";
import { useQuery } from "@tanstack/react-query";
import api from "@/lib/api";
import { Stack } from "expo-router";
import Pressable from "@/components/ui/pressable";
import Icons from "@/components/ui/icons";
export default function Hosts() {
const { toggle } = useThemeStore();
const hosts = useQuery({
queryKey: ["hosts"],
queryFn: () => api("/hosts"),
});
return (
<>
<Stack.Screen
options={{
title: "Hosts",
headerRight: () => (
<Button onPress={() => toggle()} mr="$2">
Toggle Theme
</Button>
),
}}
/>
{hosts.isLoading ? (
<View alignItems="center" justifyContent="center" flex={1}>
<Spinner size="large" />
<Text mt="$4">Loading...</Text>
</View>
) : (
<ScrollView
contentContainerStyle={{
padding: "$2",
flexDirection: "row",
flexWrap: "wrap",
// gap: "$4",
}}
>
{hosts.data.rows?.map((host: any) => (
<Pressable
key={host.id}
flexBasis="100%"
$gtXs={{ flexBasis: "50%" }}
$gtSm={{ flexBasis: "33.3%" }}
$gtMd={{ flexBasis: "25%" }}
$gtLg={{ flexBasis: "20%" }}
p="$2"
group
>
<Card elevate bordered p="$4">
<XStack>
<View flex={1}>
<Text>{host.label}</Text>
<Text fontSize="$3" mt="$2">
{host.host}
</Text>
</View>
<Button
circular
display="none"
$group-hover={{ display: "block" }}
>
<Icons name="pencil" size={16} />
</Button>
</XStack>
</Card>
</Pressable>
))}
</ScrollView>
)}
</>
);
}
+8 -1
View File
@@ -1,6 +1,13 @@
import React from "react";
import { Redirect } from "expo-router";
import { useTermSession } from "@/stores/terminal-sessions";
export default function index() {
return <Redirect href="/hosts" />;
const { sessions, curSession } = useTermSession();
return (
<Redirect
href={sessions.length > 0 && curSession >= 0 ? "/terminal" : "/hosts"}
/>
);
}
-91
View File
@@ -1,91 +0,0 @@
import { View, ScrollView, Button } from "react-native";
import React, { useState } from "react";
import { Stack } from "expo-router";
import InteractiveSession, {
InteractiveSessionProps,
} from "@/components/containers/interactive-session";
import PagerView from "@/components/ui/pager-view";
type Session = InteractiveSessionProps & { id: string };
const HomePage = () => {
const [sessions, setSessions] = useState<Session[]>([
{
id: "1",
type: "ssh",
params: { hostId: "01jc3v9w609f8e2wzw60amv195" },
},
// {
// id: "2",
// type: "pve",
// params: { client: "vnc", hostId: "01jc3wp2b3zvgr777f4e3caw4w" },
// },
// {
// id: "3",
// type: "pve",
// params: { client: "xtermjs", hostId: "01jc3z3yyn2fgb77tyfxc1tkfy" },
// },
// {
// id: "4",
// type: "incus",
// params: {
// client: "xtermjs",
// hostId: "01jc3xz9db0v54dg10hk70a13b",
// shell: "fish",
// },
// },
]);
const [curSession, setSession] = useState(0);
return (
<View style={{ flex: 1 }}>
<Stack.Screen options={{ title: "Home", headerShown: false }} />
<ScrollView
horizontal
style={{ flexGrow: 0, backgroundColor: "#111" }}
contentContainerStyle={{ flexDirection: "row", gap: 8 }}
>
{sessions.map((session, idx) => (
<View
key={session.id}
style={{ flexDirection: "row", alignItems: "center" }}
>
<Button
title={"Session " + session.id}
color="#333"
onPress={() => setSession(idx)}
/>
<Button
title="X"
onPress={() => {
const newSessions = sessions.filter((s) => s.id !== session.id);
setSessions(newSessions);
setSession(
Math.min(Math.max(curSession, 0), newSessions.length - 1)
);
}}
/>
</View>
))}
{/* <Button
title="[ + ]"
onPress={() => {
nextSession += 1;
setSessions([...sessions, nextSession.toString()]);
setSession(sessions.length);
}}
/> */}
</ScrollView>
<PagerView style={{ flex: 1 }} page={curSession}>
{sessions.map((session) => (
<InteractiveSession key={session.id} {...session} />
))}
</PagerView>
</View>
);
};
export default HomePage;
+3
View File
@@ -0,0 +1,3 @@
import SessionsPage from "@/pages/terminal/sessions-page";
export default SessionsPage;