Implement basic API interaction

This commit is contained in:
april
2024-01-02 17:41:11 -06:00
parent a456f8155b
commit 73b11482ff
20 changed files with 2867 additions and 189 deletions

View File

@@ -1,41 +1,5 @@
import type { MetaFunction } from "@remix-run/node";
import { Outlet } from "@remix-run/react";
export const meta: MetaFunction = () => {
return [
{ title: "New Remix App" },
{ name: "description", content: "Welcome to Remix!" },
];
};
export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>Welcome to Remix</h1>
<ul>
<li>
<a
target="_blank"
href="https://remix.run/tutorials/blog"
rel="noreferrer"
>
15m Quickstart Blog Tutorial
</a>
</li>
<li>
<a
target="_blank"
href="https://remix.run/tutorials/jokes"
rel="noreferrer"
>
Deep Dive Jokes App Tutorial
</a>
</li>
<li>
<a target="_blank" href="https://remix.run/docs" rel="noreferrer">
Remix Docs
</a>
</li>
</ul>
</div>
);
export default function Tailfin() {
return <Outlet />;
}

View File

@@ -0,0 +1,40 @@
import { client } from "@/util/api";
import { List, Stack, Text } from "@mantine/core";
import { useParams } from "@remix-run/react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
export default function Flight() {
const params = useParams();
const queryClient = useQueryClient();
const flight = useQuery({
queryKey: [params.id],
queryFn: async () =>
await client.get(`/flights/${params.id}`).then((res) => res.data),
});
return (
<Stack h="calc(100vh - 95px)" m="0" p="0">
{flight.isError ? (
<Text c="red">Error fetching flight</Text>
) : flight.isPending ? (
<Text>Loading...</Text>
) : (
<List>
{Object.entries(flight.data).map(([key, value]) =>
value && value.length !== 0 ? (
<List.Item key={key}>
<Text span>
<Text span fw={700}>
{key}
</Text>
: <Text span>{value}</Text>
</Text>
</List.Item>
) : null
)}
</List>
)}
</Stack>
);
}

View File

@@ -0,0 +1,19 @@
import { Center, Container, Stack } from "@mantine/core";
import { MobileFlightsList } from "@/routes/logbook.flights/flights-list";
import { IconFeather } from "@tabler/icons-react";
export default function Flights() {
return (
<>
<Container visibleFrom="md" h="calc(100vh - 95px)">
<Stack align="center" justify="center" h="100%">
<IconFeather size="3rem" />
<Center>Select a flight</Center>
</Stack>
</Container>
<Container hiddenFrom="md">
<MobileFlightsList />
</Container>
</>
);
}

View File

@@ -0,0 +1,85 @@
import { client } from "@/util/api";
import {
Divider,
NavLink,
Text,
Container,
Button,
ScrollArea,
Stack,
} from "@mantine/core";
import { Link, useLocation } from "@remix-run/react";
import { IconPlus } from "@tabler/icons-react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
export function FlightsList() {
const queryClient = useQueryClient();
const flights = useQuery({
queryKey: ["flights-list"],
queryFn: () => client.get(`/flights`).then((res) => res.data),
});
const location = useLocation();
const page = location.pathname.split("/")[3];
return (
<Stack p="0" m="0" gap="0">
<Button variant="outline" leftSection={<IconPlus />} mb="md">
Add
</Button>
<ScrollArea>
{flights.data ? (
flights.data.map((flight, index) => (
<NavLink
key={index}
component={Link}
to={`/logbook/flights/${flight.id}`}
label={`${flight.date}`}
active={page === flight.id}
/>
))
) : (
<Text p="sm">No Flights</Text>
)}
</ScrollArea>
</Stack>
);
}
export function MobileFlightsList() {
const queryClient = useQueryClient();
const flights = useQuery({
queryKey: ["flights-list"],
queryFn: () => client.get(`/flights`).then((res) => res.data),
});
const location = useLocation();
const page = location.pathname.split("/")[3];
return (
<Stack p="0" m="0" justify="space-between" h="calc(100vh - 95px)">
<ScrollArea h="calc(100vh - 95px - 50px">
{flights.data ? (
flights.data.map((flight, index) => (
<NavLink
key={index}
component={Link}
to={`/logbook/flights/${flight.id}`}
label={`${flight.date}`}
active={page === flight.id}
/>
))
) : (
<Text p="sm">No Flights</Text>
)}
</ScrollArea>
<Button variant="outline" leftSection={<IconPlus />} mt="md">
Add
</Button>
</Stack>
);
}
export default { FlightsList, MobileFlightsList };

View File

@@ -0,0 +1,22 @@
import { Divider, Grid, Container } from "@mantine/core";
import { Outlet } from "@remix-run/react";
import { FlightsList } from "./flights-list";
export default function FlightsLayout() {
return (
<>
<Grid h="100%" visibleFrom="md">
<Grid.Col span={3}>
<FlightsList />
</Grid.Col>
<Divider orientation="vertical" m="sm" />
<Grid.Col span="auto">
<Outlet />
</Grid.Col>
</Grid>
<Container hiddenFrom="md">
<Outlet />
</Container>
</>
);
}

View File

@@ -0,0 +1,12 @@
import { useMe } from "@/util/hooks";
import { Container, Title } from "@mantine/core";
export default function Me() {
const me = useMe();
return (
<Container>
<Title order={2}>{me.data?.username}</Title>
</Container>
);
}

View File

@@ -0,0 +1,20 @@
import { TailfinAppShell } from "@/ui/nav/app-shell";
import type { MetaFunction } from "@remix-run/node";
import { Outlet } from "@remix-run/react";
export const meta: MetaFunction = () => {
return [
{ title: "Tailfin" },
{ name: "description", content: "Self-hosted flight logbook" },
];
};
export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<TailfinAppShell>
<Outlet />
</TailfinAppShell>
</div>
);
}

View File

@@ -0,0 +1,52 @@
import { client } from "@/util/api";
import { useLogin } from "@/util/hooks";
import {
Box,
Button,
Group,
PasswordInput,
Stack,
TextInput,
Title,
} from "@mantine/core";
import { useForm } from "@mantine/form";
export default function Login() {
const form = useForm({
initialValues: {
username: "",
password: "",
},
});
const signInMutation = useLogin();
return (
<Stack gap="md" h="100%" justify="center" align="stretch">
<Title order={2} style={{ textAlign: "center" }}>
Login
</Title>
<Box maw={340} mx="auto">
<form
onSubmit={form.onSubmit((values) => {
signInMutation(values);
})}
>
<TextInput
label="Username"
{...form.getInputProps("username")}
mt="md"
/>
<PasswordInput
label="Password"
{...form.getInputProps("password")}
mt="md"
/>
<Group justify="center" mt="xl">
<Button type="submit">Log In</Button>
</Group>
</form>
</Box>
</Stack>
);
}