feat(cover): frontend for requesting cover letters
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import renderQueryState from "@/components/RenderQueryState";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useAppForm } from "@/hooks/formHook";
|
||||
import Authorised from "@/layouts/Authorised";
|
||||
import requests from "@/lib/requests";
|
||||
import type { Template } from "@/types/api";
|
||||
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/cover/create")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const templates = useQuery({
|
||||
queryKey: ["user_templates"],
|
||||
queryFn: () => requests.get<Template[]>("/templates", {}),
|
||||
});
|
||||
const templateState = renderQueryState({
|
||||
query: templates,
|
||||
noFound: "templates",
|
||||
skeleton: { count: 1, className: "h-16" },
|
||||
});
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
templateId: "",
|
||||
application: "Paste job application here",
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Authorised>
|
||||
<h1 className="text-2xl font-bold text-primary">Create new cover letter</h1>
|
||||
<div className="border p-4 mt-4 rounded-md">
|
||||
{templateState !== null ? (
|
||||
templateState
|
||||
) : (
|
||||
<form.AppField
|
||||
name="templateId"
|
||||
children={(f) => (
|
||||
<f.SelectField
|
||||
data={templates.data?.map((t) => ({ value: t.id, name: t.name }))}
|
||||
label={"Select template for cover letter"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<form.AppField name="application" children={(f) => <f.RichTextEdit />} />
|
||||
</div>
|
||||
|
||||
<Button className="mt-4">Generate cover letter</Button>
|
||||
</Authorised>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import Authorised from "@/layouts/Authorised";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Plus } from "lucide-react";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: App,
|
||||
@@ -8,7 +10,15 @@ export const Route = createFileRoute("/")({
|
||||
function App() {
|
||||
return (
|
||||
<Authorised>
|
||||
<h1>Welcome to cover letter</h1>
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold text-primary">0 Cover letters</h1>
|
||||
|
||||
<Link to="/cover/create">
|
||||
<Button icon={<Plus />} variant="secondary">
|
||||
Create new
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</Authorised>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,40 +6,21 @@ import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Plus } from "lucide-react";
|
||||
import type { Template } from "@/types/api";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import renderQueryState from "@/components/RenderQueryState";
|
||||
|
||||
export const Route = createFileRoute("/templates/")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RenderTemplates({ templates }: { templates: UseQueryResult<null | Template[], Error> }) {
|
||||
// Render loading
|
||||
if (templates.isPending) {
|
||||
const skelets = new Array(5).fill(null);
|
||||
return skelets.map((_, i) => <Skeleton className="h-10" key={i} />);
|
||||
}
|
||||
|
||||
// Render error
|
||||
if (templates.isError) {
|
||||
return <div className="text-danger font-bold">Error: {templates.error.message}</div>;
|
||||
}
|
||||
|
||||
// Render null
|
||||
if (templates.data === null) {
|
||||
return <div className="text-primary">No templates found</div>;
|
||||
}
|
||||
|
||||
return templates.data.map((template, i) => (
|
||||
<div className="flex gap-2 items-center" key={i}>
|
||||
<p className="text-lg">{template.name}</p>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
function RouteComponent() {
|
||||
const templates = useQuery({
|
||||
queryKey: ["user_templates"],
|
||||
queryFn: () => requests.get<Template[]>("/templates", {}),
|
||||
});
|
||||
const templatesState = renderQueryState({
|
||||
query: templates,
|
||||
noFound: "templates",
|
||||
});
|
||||
|
||||
return (
|
||||
<Authorised>
|
||||
@@ -54,7 +35,13 @@ function RouteComponent() {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 mt-4">
|
||||
<RenderTemplates templates={templates} />
|
||||
{templatesState !== null
|
||||
? templatesState
|
||||
: templates.data?.map((template, i) => (
|
||||
<div className="flex gap-2 items-center" key={i}>
|
||||
<p className="text-lg">{template.name}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Authorised>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user