Split some code into seperate components
This commit is contained in:
parent
dddd9fd666
commit
42c2d069ef
@ -1,10 +1,13 @@
|
|||||||
import { Link, Stack, useSearchParams } from "expo-router";
|
import { Link, useSearchParams } from "expo-router";
|
||||||
import { View, Text, SafeAreaView, ScrollView } from "react-native";
|
import { View, Text, SafeAreaView, ScrollView, Button } from "react-native";
|
||||||
import { NodeResource, useNode } from "../../../hooks/useNode";
|
import { NodeResource, useNode } from "../../../hooks/useNode";
|
||||||
import Icon from "@expo/vector-icons/Feather";
|
import Icon from "@expo/vector-icons/Feather";
|
||||||
import tw from "twrnc";
|
import tw from "twrnc";
|
||||||
import { formatBytes, formatPercentage } from "../../../lib/helper/format";
|
import { formatBytes, formatPercentage } from "../../../lib/helper/format";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
|
import { Gauge } from "../../../components/Gauge";
|
||||||
|
import ProgressBar from "../../../components/ProgressBar";
|
||||||
|
import Card from "../../../components/Card";
|
||||||
|
|
||||||
interface ResourceListItemProps {
|
interface ResourceListItemProps {
|
||||||
type: "LXC" | "QEMU";
|
type: "LXC" | "QEMU";
|
||||||
@ -42,54 +45,6 @@ export function ResourceListItem({ type, resource }: ResourceListItemProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProgressBarProps {
|
|
||||||
label: string;
|
|
||||||
value: number;
|
|
||||||
max: number;
|
|
||||||
formatFn?: (input: number) => string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ProgressBar({ label, value, max, formatFn }: ProgressBarProps) {
|
|
||||||
const percentage = formatPercentage(value / max);
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<View
|
|
||||||
style={tw.style("flex flex-row justify-between items-center mb-1 px-1")}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{label} ({percentage})
|
|
||||||
</Text>
|
|
||||||
{formatFn && (
|
|
||||||
<Text>
|
|
||||||
{formatFn(value)}/{formatFn(max)}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
<View style={tw.style("h-3 w-full bg-slate-300 rounded-lg")}>
|
|
||||||
<View
|
|
||||||
style={tw.style("h-full bg-slate-700 rounded-lg", {
|
|
||||||
width: percentage,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GaugeProps {
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Gauge({ label, value }: GaugeProps) {
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<Text>{label}</Text>
|
|
||||||
<Text style={tw.style("text-2xl")}>{value}</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NodePage() {
|
export default function NodePage() {
|
||||||
const { node: nodeName } = useSearchParams<{ node: string }>();
|
const { node: nodeName } = useSearchParams<{ node: string }>();
|
||||||
const node = useNode(nodeName);
|
const node = useNode(nodeName);
|
||||||
@ -105,14 +60,10 @@ export default function NodePage() {
|
|||||||
}, [node.qemu.data]);
|
}, [node.qemu.data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
|
||||||
<SafeAreaView>
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View
|
<Card>
|
||||||
style={tw.style(
|
<View style={tw.style("p-1")}>
|
||||||
"bg-white m-2 p-3 rounded-lg border border-slate-200"
|
<View style={tw.style("px-1")}>
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Text style={tw.style("text-2xl font-semibold")}>{nodeName}</Text>
|
<Text style={tw.style("text-2xl font-semibold")}>{nodeName}</Text>
|
||||||
{node.status.isSuccess && (
|
{node.status.isSuccess && (
|
||||||
<View style={tw.style("mb-4")}>
|
<View style={tw.style("mb-4")}>
|
||||||
@ -120,6 +71,7 @@ export default function NodePage() {
|
|||||||
<Text>{node.status.data.kversion}</Text>
|
<Text>{node.status.data.kversion}</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
</View>
|
||||||
{node.rdd.isSuccess && (
|
{node.rdd.isSuccess && (
|
||||||
<View>
|
<View>
|
||||||
<View style={tw.style("mb-2")}>
|
<View style={tw.style("mb-2")}>
|
||||||
@ -162,12 +114,8 @@ export default function NodePage() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Text style={tw.style("ml-6 mt-4")}>LXC Containers</Text>
|
</Card>
|
||||||
<View
|
<Card label="LXC Containers">
|
||||||
style={tw.style(
|
|
||||||
"bg-white m-2 p-1 rounded-lg border border-slate-200"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{sortedLXCs.map((lxc) => (
|
{sortedLXCs.map((lxc) => (
|
||||||
<Link
|
<Link
|
||||||
key={lxc.vmid}
|
key={lxc.vmid}
|
||||||
@ -180,13 +128,8 @@ export default function NodePage() {
|
|||||||
<ResourceListItem type="LXC" resource={lxc} />
|
<ResourceListItem type="LXC" resource={lxc} />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</View>
|
</Card>
|
||||||
<Text style={tw.style("ml-6 mt-4")}>Virtual Machines</Text>
|
<Card label="Virtual Machine">
|
||||||
<View
|
|
||||||
style={tw.style(
|
|
||||||
"bg-white m-2 p-1 rounded-lg border border-slate-200"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{sortedVMs.map((vm) => (
|
{sortedVMs.map((vm) => (
|
||||||
<Link
|
<Link
|
||||||
key={vm.vmid}
|
key={vm.vmid}
|
||||||
@ -199,9 +142,7 @@ export default function NodePage() {
|
|||||||
<ResourceListItem type="QEMU" resource={vm} />
|
<ResourceListItem type="QEMU" resource={vm} />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</View>
|
</Card>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import Icon from "@expo/vector-icons/Feather";
|
|||||||
import tw from "twrnc";
|
import tw from "twrnc";
|
||||||
import { formatPercentage } from "../../lib/helper/format";
|
import { formatPercentage } from "../../lib/helper/format";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
import { ScrollView } from "react-native-gesture-handler";
|
||||||
|
import Card from "../../components/Card";
|
||||||
|
|
||||||
export function NodeListItem({ node }: { node: ProxmoxNode }) {
|
export function NodeListItem({ node }: { node: ProxmoxNode }) {
|
||||||
return (
|
return (
|
||||||
@ -38,15 +39,8 @@ export default function HomePage() {
|
|||||||
const nodes = useNodes();
|
const nodes = useNodes();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<SafeAreaView>
|
<Card label="Nodes">
|
||||||
<Text style={tw.style("ml-6 mt-4")}>Nodes</Text>
|
|
||||||
<View
|
|
||||||
style={tw.style(
|
|
||||||
"bg-white m-2 p-1 rounded-lg border border-slate-200"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{nodes.isSuccess &&
|
{nodes.isSuccess &&
|
||||||
nodes.data.map((node) => (
|
nodes.data.map((node) => (
|
||||||
<Link
|
<Link
|
||||||
@ -61,20 +55,15 @@ export default function HomePage() {
|
|||||||
<NodeListItem node={node} />
|
<NodeListItem node={node} />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</View>
|
</Card>
|
||||||
|
|
||||||
<Text style={tw.style("ml-6 mt-4")}>Storage</Text>
|
<Card label="Storage">
|
||||||
<View
|
<View
|
||||||
style={tw.style(
|
style={tw.style("flex flex-row justify-center items-center h-16")}
|
||||||
"bg-white m-2 p-1 rounded-lg border border-slate-200"
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<Text style={tw.style("px-2 py-7 flex flex-row text-center")}>
|
<Text>Currently not supported</Text>
|
||||||
Currently not supported
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</Card>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
20
components/Card/index.tsx
Normal file
20
components/Card/index.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { View, Text } from "react-native";
|
||||||
|
import tw from "twrnc";
|
||||||
|
|
||||||
|
interface CardProps {
|
||||||
|
label?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Card({ label, children }: CardProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{label && <Text style={tw.style("ml-6 mt-4")}>{label}</Text>}
|
||||||
|
<View
|
||||||
|
style={tw.style("bg-white m-2 p-1 rounded-lg border border-slate-200")}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
16
components/Gauge/index.tsx
Normal file
16
components/Gauge/index.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { View, Text } from "react-native";
|
||||||
|
import tw from "twrnc";
|
||||||
|
|
||||||
|
interface GaugeProps {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Gauge({ label, value }: GaugeProps) {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text>{label}</Text>
|
||||||
|
<Text style={tw.style("text-2xl")}>{value}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
@ -1,12 +1,49 @@
|
|||||||
import { View, Button, Text, TextInput } from "react-native";
|
import {
|
||||||
|
View,
|
||||||
|
TouchableOpacity,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
SafeAreaView,
|
||||||
|
TextInputProps,
|
||||||
|
} from "react-native";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import tw from "twrnc";
|
||||||
import useAuthStore from "../../stores/useAuthStore";
|
import useAuthStore from "../../stores/useAuthStore";
|
||||||
import { useTicketMut } from "../../hooks/useTicket";
|
import { useTicketMut } from "../../hooks/useTicket";
|
||||||
|
|
||||||
|
interface FormFieldProps extends TextInputProps {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FormField({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
onChangeText,
|
||||||
|
placeholder,
|
||||||
|
secureTextEntry,
|
||||||
|
inputMode,
|
||||||
|
}: FormFieldProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text style={tw.style("ml-2 mb-2 text-slate-500")}>{label}</Text>
|
||||||
|
<TextInput
|
||||||
|
style={tw.style(
|
||||||
|
"border rounded-md border-slate-200 bg-slate-100 px-4 py-2 text-base"
|
||||||
|
)}
|
||||||
|
value={value}
|
||||||
|
onChangeText={onChangeText}
|
||||||
|
placeholder={placeholder}
|
||||||
|
secureTextEntry={secureTextEntry}
|
||||||
|
inputMode={inputMode}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const [domain, setDomain] = useState("");
|
const [domain, setDomain] = useState("https://pve.holowaif.us:8006");
|
||||||
const [username, setUsername] = useState("root@pam");
|
const [username, setUsername] = useState("root@pam");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("1Absolut3.lyAd0r3Vtube.rs!");
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const ticketMut = useTicketMut({
|
const ticketMut = useTicketMut({
|
||||||
@ -17,23 +54,51 @@ export default function Login() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View style={tw.style("flex-1 justify-center")}>
|
||||||
<Text>Hello</Text>
|
<SafeAreaView style={tw.style("mx-4")}>
|
||||||
<TextInput value={domain} onChangeText={setDomain} placeholder="Domain" />
|
<View style={tw.style("ml-2 mb-8")}>
|
||||||
<TextInput
|
<Text style={tw.style("text-3xl font-semibold text-slate-700 mb-2")}>
|
||||||
|
PVERN
|
||||||
|
</Text>
|
||||||
|
<Text style={tw.style("text-xl font-semibold text-slate-600")}>
|
||||||
|
Sign In
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={tw.style("mb-5")}>
|
||||||
|
<FormField
|
||||||
|
inputMode="url"
|
||||||
|
label="Server URL"
|
||||||
|
placeholder="https://pve.example.com"
|
||||||
|
value={domain}
|
||||||
|
onChangeText={setDomain}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={tw.style("mb-5")}>
|
||||||
|
<FormField
|
||||||
|
autoComplete="username"
|
||||||
|
label="Username"
|
||||||
value={username}
|
value={username}
|
||||||
onChangeText={setUsername}
|
onChangeText={setUsername}
|
||||||
placeholder="Username"
|
|
||||||
/>
|
/>
|
||||||
<TextInput
|
</View>
|
||||||
|
<View style={tw.style("mb-10")}>
|
||||||
|
<FormField
|
||||||
|
autoComplete="password"
|
||||||
|
label="Password"
|
||||||
value={password}
|
value={password}
|
||||||
onChangeText={setPassword}
|
onChangeText={setPassword}
|
||||||
placeholder="Password"
|
secureTextEntry
|
||||||
/>
|
/>
|
||||||
<Button
|
</View>
|
||||||
title="Login"
|
<TouchableOpacity
|
||||||
|
style={tw.style(
|
||||||
|
"flex bg-slate-700 rounded-md flex flex-row items-center justify-center"
|
||||||
|
)}
|
||||||
onPress={() => ticketMut.mutate({ domain, username, password })}
|
onPress={() => ticketMut.mutate({ domain, username, password })}
|
||||||
/>
|
>
|
||||||
|
<Text style={tw.style("text-white font-semibold py-3")}>Sign In</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</SafeAreaView>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
42
components/ProgressBar/index.tsx
Normal file
42
components/ProgressBar/index.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { View, Text } from "react-native";
|
||||||
|
import tw from "twrnc";
|
||||||
|
import { formatPercentage } from "../../lib/helper/format";
|
||||||
|
|
||||||
|
interface ProgressBarProps {
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
max: number;
|
||||||
|
formatFn?: (input: number) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ProgressBar({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
max,
|
||||||
|
formatFn,
|
||||||
|
}: ProgressBarProps) {
|
||||||
|
const percentage = formatPercentage(value / max);
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View
|
||||||
|
style={tw.style("flex flex-row justify-between items-center mb-1 px-1")}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
{label} ({percentage})
|
||||||
|
</Text>
|
||||||
|
{formatFn && (
|
||||||
|
<Text>
|
||||||
|
{formatFn(value)}/{formatFn(max)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<View style={tw.style("h-3 w-full bg-slate-300 rounded-lg")}>
|
||||||
|
<View
|
||||||
|
style={tw.style("h-full bg-slate-700 rounded-lg", {
|
||||||
|
width: percentage,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user