mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 02:33:01 +00:00
IssueResponse definition and implementation
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import type { IssueRecord, ProjectRecord } from "@issue/shared";
|
import type { IssueResponse, ProjectRecord } from "@issue/shared";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { IssueDetailPane } from "@/components/issue-detail-pane";
|
import { IssueDetailPane } from "@/components/issue-detail-pane";
|
||||||
import { IssuesTable } from "@/components/issues-table";
|
import { IssuesTable } from "@/components/issues-table";
|
||||||
@@ -23,8 +23,8 @@ function Index() {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [selectedIssue, setSelectedIssue] = useState<IssueRecord | null>(null);
|
const [selectedIssue, setSelectedIssue] = useState<IssueResponse | null>(null);
|
||||||
const [issues, setIssues] = useState<IssueRecord[]>([]);
|
const [issuesData, setIssues] = useState<IssueResponse[]>([]);
|
||||||
|
|
||||||
const serverURL = import.meta.env.SERVER_URL?.trim() || "http://localhost:3000";
|
const serverURL = import.meta.env.SERVER_URL?.trim() || "http://localhost:3000";
|
||||||
|
|
||||||
@@ -33,8 +33,9 @@ function Index() {
|
|||||||
|
|
||||||
fetch(`${serverURL}/issues/${selectedProject.blob}`)
|
fetch(`${serverURL}/issues/${selectedProject.blob}`)
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((data: IssueRecord[]) => {
|
.then((data: IssueResponse[]) => {
|
||||||
setIssues(data);
|
setIssues(data);
|
||||||
|
console.log(data);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("error fetching issues:", err);
|
console.error("error fetching issues:", err);
|
||||||
@@ -78,12 +79,12 @@ function Index() {
|
|||||||
</div>
|
</div>
|
||||||
{/* main body */}
|
{/* main body */}
|
||||||
<div className="w-full h-full flex items-start justify-between pt-2 gap-2">
|
<div className="w-full h-full flex items-start justify-between pt-2 gap-2">
|
||||||
{selectedProject && issues.length > 0 && (
|
{selectedProject && issuesData.length > 0 && (
|
||||||
<>
|
<>
|
||||||
{/* issues list (table) */}
|
{/* issues list (table) */}
|
||||||
<IssuesTable
|
<IssuesTable
|
||||||
project={selectedProject}
|
project={selectedProject}
|
||||||
issues={issues}
|
issuesData={issuesData}
|
||||||
columns={{ description: false }}
|
columns={{ description: false }}
|
||||||
issueSelectAction={setSelectedIssue}
|
issueSelectAction={setSelectedIssue}
|
||||||
className="border w-full flex-shrink"
|
className="border w-full flex-shrink"
|
||||||
@@ -93,7 +94,7 @@ function Index() {
|
|||||||
<div className="border w-2xl">
|
<div className="border w-2xl">
|
||||||
<IssueDetailPane
|
<IssueDetailPane
|
||||||
project={selectedProject}
|
project={selectedProject}
|
||||||
issue={selectedIssue}
|
issueData={selectedIssue}
|
||||||
close={() => setSelectedIssue(null)}
|
close={() => setSelectedIssue(null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import type { IssueRecord, ProjectRecord } from "@issue/shared";
|
import type { IssueResponse, ProjectRecord } from "@issue/shared";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { issueID } from "@/lib/utils";
|
import { issueID } from "@/lib/utils";
|
||||||
|
|
||||||
export function IssueDetailPane({
|
export function IssueDetailPane({
|
||||||
project,
|
project,
|
||||||
issue,
|
issueData,
|
||||||
close,
|
close,
|
||||||
}: {
|
}: {
|
||||||
project: ProjectRecord;
|
project: ProjectRecord;
|
||||||
issue: IssueRecord;
|
issueData: IssueResponse;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row items-center justify-end border-b">
|
<div className="flex flex-row items-center justify-end border-b">
|
||||||
<span className="w-full px-0.5">
|
<span className="w-full px-0.5">
|
||||||
<p className="text-sm w-fit px-0.75">{issueID(project.blob, issue.number)}</p>
|
<p className="text-sm w-fit px-0.75">{issueID(project.blob, issueData.Issue.number)}</p>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<Button variant={"dummy"} onClick={close} className="px-0 py-0 w-6 h-6">
|
<Button variant={"dummy"} onClick={close} className="px-0 py-0 w-6 h-6">
|
||||||
@@ -25,8 +25,13 @@ export function IssueDetailPane({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col w-full p-2 gap-2">
|
<div className="flex flex-col w-full p-2 gap-2">
|
||||||
<h1 className="text-md">{issue.title}</h1>
|
<h1 className="text-md">{issueData.Issue.title}</h1>
|
||||||
<p className="text-sm">{issue.description}</p>
|
<p className="text-sm">{issueData.Issue.description}</p>
|
||||||
|
|
||||||
|
<p className="text-sm">
|
||||||
|
Assignee:{" "}
|
||||||
|
{issueData.User ? `${issueData.User.name} (${issueData.User.username})` : "Unassigned"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import type { IssueRecord, ProjectRecord } from "@issue/shared";
|
import type { IssueResponse, ProjectRecord } from "@issue/shared";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
import { cn, issueID } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function IssuesTable({
|
export function IssuesTable({
|
||||||
project,
|
project,
|
||||||
issues,
|
issuesData,
|
||||||
columns = {},
|
columns = {},
|
||||||
issueSelectAction,
|
issueSelectAction,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
project: ProjectRecord;
|
project: ProjectRecord;
|
||||||
issues: IssueRecord[];
|
issuesData: IssueResponse[];
|
||||||
columns?: { id?: boolean; title?: boolean; description?: boolean; assignee?: boolean };
|
columns?: { id?: boolean; title?: boolean; description?: boolean; assignee?: boolean };
|
||||||
issueSelectAction?: (issue: IssueRecord) => void;
|
issueSelectAction?: (issue: IssueResponse) => void;
|
||||||
className: string;
|
className: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -31,27 +31,27 @@ export function IssuesTable({
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{issues.map((issue) => (
|
{issuesData.map((issueData) => (
|
||||||
<TableRow
|
<TableRow
|
||||||
key={issue.id}
|
key={issueData.Issue.id}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
issueSelectAction?.(issue);
|
issueSelectAction?.(issueData);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(columns.id == null || columns.id === true) && (
|
{(columns.id == null || columns.id === true) && (
|
||||||
<TableCell className="font-medium border-r text-right">
|
<TableCell className="font-medium border-r text-right">
|
||||||
{issue.number.toString().padStart(3, "0")}
|
{issueData.Issue.number.toString().padStart(3, "0")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
{(columns.title == null || columns.title === true) && (
|
{(columns.title == null || columns.title === true) && (
|
||||||
<TableCell>{issue.title}</TableCell>
|
<TableCell>{issueData.Issue.title}</TableCell>
|
||||||
)}
|
)}
|
||||||
{(columns.description == null || columns.description === true) && (
|
{(columns.description == null || columns.description === true) && (
|
||||||
<TableCell className="overflow-hide">{issue.description}</TableCell>
|
<TableCell className="overflow-hide">{issueData.Issue.description}</TableCell>
|
||||||
)}
|
)}
|
||||||
{(columns.assignee == null || columns.assignee === true) && (
|
{(columns.assignee == null || columns.assignee === true) && (
|
||||||
<TableCell className={"text-right"}>?</TableCell>
|
<TableCell className={"text-right"}>{issueData.User?.id || "?"}</TableCell>
|
||||||
)}
|
)}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -20,3 +20,8 @@ export {
|
|||||||
IssueSelectSchema,
|
IssueSelectSchema,
|
||||||
IssueInsertSchema,
|
IssueInsertSchema,
|
||||||
} from "./schema";
|
} from "./schema";
|
||||||
|
|
||||||
|
// Responses
|
||||||
|
export {
|
||||||
|
IssueResponse,
|
||||||
|
} from "./schema"
|
||||||
@@ -58,3 +58,10 @@ export type ProjectInsert = z.infer<typeof ProjectInsertSchema>;
|
|||||||
|
|
||||||
export type IssueRecord = z.infer<typeof IssueSelectSchema>;
|
export type IssueRecord = z.infer<typeof IssueSelectSchema>;
|
||||||
export type IssueInsert = z.infer<typeof IssueInsertSchema>;
|
export type IssueInsert = z.infer<typeof IssueInsertSchema>;
|
||||||
|
|
||||||
|
// Responses
|
||||||
|
|
||||||
|
export type IssueResponse = {
|
||||||
|
Issue: IssueRecord;
|
||||||
|
User?: UserRecord;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user