From d0de05e0a2f21f2c859495e6ec344e5914aa013d Mon Sep 17 00:00:00 2001 From: Leons Aleksandrovs <58330666+Skrazzo@users.noreply.github.com> Date: Sat, 12 Jul 2025 17:11:11 +0300 Subject: [PATCH] feat(cover): Display generated cover letters --- backend/controllers/cover/cover.go | 2 +- backend/controllers/template/template.go | 2 +- frontend/src/components/ui/container.tsx | 2 +- frontend/src/editor.css | 5 +++ frontend/src/routeTree.gen.ts | 21 +++++++++++ frontend/src/routes/cover/$coverId.tsx | 48 ++++++++++++++++++++++++ frontend/src/routes/index.tsx | 32 +++++++++++++++- frontend/src/types/api.ts | 12 ++++++ 8 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 frontend/src/routes/cover/$coverId.tsx diff --git a/backend/controllers/cover/cover.go b/backend/controllers/cover/cover.go index 5aa6280..e24e3d9 100644 --- a/backend/controllers/cover/cover.go +++ b/backend/controllers/cover/cover.go @@ -29,7 +29,7 @@ func Get(c *gin.Context) { return } - covers, err := cover.Get("user_id = $1", user.Id) + covers, err := cover.Get("user_id = $1 ORDER BY created_at DESC", user.Id) if err != nil { res.Error(c, err.Error(), http.StatusInternalServerError) return diff --git a/backend/controllers/template/template.go b/backend/controllers/template/template.go index c0dd33a..2a86471 100644 --- a/backend/controllers/template/template.go +++ b/backend/controllers/template/template.go @@ -69,7 +69,7 @@ func Get(c *gin.Context) { } // Get all user templates - templates, err := template.Get("user_id = $1", user.Id) + templates, err := template.Get("user_id = $1 ORDER BY created_at DESC", user.Id) if err != nil { res.Error(c, err.Error(), http.StatusInternalServerError) return diff --git a/frontend/src/components/ui/container.tsx b/frontend/src/components/ui/container.tsx index ac40829..59bf855 100644 --- a/frontend/src/components/ui/container.tsx +++ b/frontend/src/components/ui/container.tsx @@ -1,3 +1,3 @@ export default function Container({ children, className = "" }: React.ComponentProps<"div">) { - return
{children}
; + return
{children}
; } diff --git a/frontend/src/editor.css b/frontend/src/editor.css index 48f9d85..9a0a5aa 100644 --- a/frontend/src/editor.css +++ b/frontend/src/editor.css @@ -24,6 +24,11 @@ margin-top: 0; } + /* Text */ + p { + margin-top: 0.5rem; + } + /* Links */ a { color: var(--editor-accent); diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 0242fff..dfbcd86 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -15,6 +15,7 @@ import { Route as IndexRouteImport } from './routes/index' import { Route as TemplatesIndexRouteImport } from './routes/templates/index' import { Route as TemplatesCreateRouteImport } from './routes/templates/create' import { Route as CoverCreateRouteImport } from './routes/cover/create' +import { Route as CoverCoverIdRouteImport } from './routes/cover/$coverId' const RegisterRoute = RegisterRouteImport.update({ id: '/register', @@ -46,11 +47,17 @@ const CoverCreateRoute = CoverCreateRouteImport.update({ path: '/cover/create', getParentRoute: () => rootRouteImport, } as any) +const CoverCoverIdRoute = CoverCoverIdRouteImport.update({ + id: '/cover/$coverId', + path: '/cover/$coverId', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/login': typeof LoginRoute '/register': typeof RegisterRoute + '/cover/$coverId': typeof CoverCoverIdRoute '/cover/create': typeof CoverCreateRoute '/templates/create': typeof TemplatesCreateRoute '/templates': typeof TemplatesIndexRoute @@ -59,6 +66,7 @@ export interface FileRoutesByTo { '/': typeof IndexRoute '/login': typeof LoginRoute '/register': typeof RegisterRoute + '/cover/$coverId': typeof CoverCoverIdRoute '/cover/create': typeof CoverCreateRoute '/templates/create': typeof TemplatesCreateRoute '/templates': typeof TemplatesIndexRoute @@ -68,6 +76,7 @@ export interface FileRoutesById { '/': typeof IndexRoute '/login': typeof LoginRoute '/register': typeof RegisterRoute + '/cover/$coverId': typeof CoverCoverIdRoute '/cover/create': typeof CoverCreateRoute '/templates/create': typeof TemplatesCreateRoute '/templates/': typeof TemplatesIndexRoute @@ -78,6 +87,7 @@ export interface FileRouteTypes { | '/' | '/login' | '/register' + | '/cover/$coverId' | '/cover/create' | '/templates/create' | '/templates' @@ -86,6 +96,7 @@ export interface FileRouteTypes { | '/' | '/login' | '/register' + | '/cover/$coverId' | '/cover/create' | '/templates/create' | '/templates' @@ -94,6 +105,7 @@ export interface FileRouteTypes { | '/' | '/login' | '/register' + | '/cover/$coverId' | '/cover/create' | '/templates/create' | '/templates/' @@ -103,6 +115,7 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute LoginRoute: typeof LoginRoute RegisterRoute: typeof RegisterRoute + CoverCoverIdRoute: typeof CoverCoverIdRoute CoverCreateRoute: typeof CoverCreateRoute TemplatesCreateRoute: typeof TemplatesCreateRoute TemplatesIndexRoute: typeof TemplatesIndexRoute @@ -152,6 +165,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof CoverCreateRouteImport parentRoute: typeof rootRouteImport } + '/cover/$coverId': { + id: '/cover/$coverId' + path: '/cover/$coverId' + fullPath: '/cover/$coverId' + preLoaderRoute: typeof CoverCoverIdRouteImport + parentRoute: typeof rootRouteImport + } } } @@ -159,6 +179,7 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, LoginRoute: LoginRoute, RegisterRoute: RegisterRoute, + CoverCoverIdRoute: CoverCoverIdRoute, CoverCreateRoute: CoverCreateRoute, TemplatesCreateRoute: TemplatesCreateRoute, TemplatesIndexRoute: TemplatesIndexRoute, diff --git a/frontend/src/routes/cover/$coverId.tsx b/frontend/src/routes/cover/$coverId.tsx new file mode 100644 index 0000000..a55190c --- /dev/null +++ b/frontend/src/routes/cover/$coverId.tsx @@ -0,0 +1,48 @@ +import renderQueryState from "@/components/RenderQueryState"; +import Authorised from "@/layouts/Authorised"; +import requests from "@/lib/requests"; +import type { CoverLetter } from "@/types/api"; +import { useQuery } from "@tanstack/react-query"; +import { createFileRoute } from "@tanstack/react-router"; +import "../../editor.css"; + +export const Route = createFileRoute("/cover/$coverId")({ + component: RouteComponent, +}); + +function RouteComponent() { + const { coverId } = Route.useParams(); + + const cover = useQuery({ + queryKey: ["cover", coverId], + queryFn: () => requests.get<{ cover: CoverLetter }>(`/cover/${coverId}`, {}), + }); + const coverState = renderQueryState({ + query: cover, + noFound: "cover letter", + skeleton: { + count: 1, + className: "h-[400px]", + }, + }); + + return ( + +
+

{cover.data?.cover.name || "Loading..."}

+ {/* edit buttons */} +
+ +
+ {coverState !== null ? ( + coverState + ) : ( +
+ )} +
+ + ); +} diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index 98add8e..7b0b493 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -2,16 +2,31 @@ import { createFileRoute, Link } from "@tanstack/react-router"; import Authorised from "@/layouts/Authorised"; import { Button } from "@/components/ui/button"; import { Plus } from "lucide-react"; +import { useQuery } from "@tanstack/react-query"; +import requests from "@/lib/requests"; +import type { CoverLetterPreview } from "@/types/api"; +import renderQueryState from "@/components/RenderQueryState"; export const Route = createFileRoute("/")({ component: App, }); function App() { + const letters = useQuery({ + queryKey: ["cover_letters"], + queryFn: () => requests.get<{ covers: CoverLetterPreview[] }>("/cover", {}), + }); + const lettersState = renderQueryState({ + query: letters, + noFound: "cover letters", + }); + return (
-

0 Cover letters

+

+ {letters.data?.covers.length} Cover letters +

+ +
+ {lettersState !== null + ? lettersState + : letters.data?.covers.map((l) => ( + +

{l.name}

+ + ))} +
); } diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index c8a0131..36b4ed8 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -27,3 +27,15 @@ export interface Template { template: string; created_at: string; } + +// -------- Cover letters -------- +export interface CoverLetterPreview { + id: number; + name: string; +} + +export interface CoverLetter extends CoverLetterPreview { + user_id: number; + letter: string; + created_at: string; +}