diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index fd5981f..dbad93c 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -10,6 +10,7 @@
},
"dependencies": {
"@issue/shared": "workspace:*",
+ "@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.4",
"@tailwindcss/vite": "^4.1.18",
"@tauri-apps/api": "^2",
diff --git a/packages/frontend/src/Index.tsx b/packages/frontend/src/Index.tsx
index 0bfc631..f85bd01 100644
--- a/packages/frontend/src/Index.tsx
+++ b/packages/frontend/src/Index.tsx
@@ -1,55 +1,85 @@
-import { CloudSync, RefreshCw } from "lucide-react";
-import { useState } from "react";
+import { useEffect, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
-import { IssueRecord } from "@issue/shared";
+import type { IssueRecord, ProjectRecord } from "@issue/shared";
+import {
+ Table,
+ TableBody,
+ TableCaption,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
-function Issue({ issue }: { issue: IssueRecord }) {
+function IssueRow({ issue }: { issue: IssueRecord }) {
return (
-
- [{issue.id}] {issue.title}
-
+
+ {issue.id}
+ {issue.title}
+ {issue.description}
+
);
}
function Index() {
+ const [projects, setProjects] = useState([]);
+ const projectsRef = useRef(false);
+
+ useEffect(() => {
+ if (projectsRef.current) return;
+ projectsRef.current = true;
+
+ fetch("http://localhost:3000/projects/all")
+ .then((res) => res.json())
+ .then((data: ProjectRecord[]) => {
+ setProjects(data);
+ console.log("fetched projects:", data);
+ })
+ .catch((err) => {
+ console.error("error fetching projects:", err);
+ });
+ }, []);
+
const [issues, setIssues] = useState([]);
+ const issuesRef = useRef(false);
const serverURL = import.meta.env.SERVER_URL?.trim() || "http://localhost:3000";
- async function getIssues() {
- const res = await fetch(`${serverURL}/issues/all`);
- const data = await res.json();
- setIssues(data);
- }
+ useEffect(() => {
+ if (issuesRef.current) return;
+ issuesRef.current = true;
+
+ fetch(`${serverURL}/issues/all`)
+ .then((res) => res.json())
+ .then((data: IssueRecord[]) => {
+ setIssues(data);
+ console.log("fetched issues:", data);
+ })
+ .catch((err) => {
+ console.error("error fetching issues:", err);
+ });
+ }, []);
return (
-
- Issue Project Manager
-
-
-
-
+
+
+
+ All Issues
+
+
+ a
+ a
+ a
+ a
+
+
+
+ {issues.map((issue) => (
+
+ ))}
+
+
-
- {issues.length > 0 && (
-
- {JSON.stringify(issues, null, 2)}
-
- )}
);
}
diff --git a/packages/frontend/src/components/ui/select.tsx b/packages/frontend/src/components/ui/select.tsx
new file mode 100644
index 0000000..b8aab97
--- /dev/null
+++ b/packages/frontend/src/components/ui/select.tsx
@@ -0,0 +1,188 @@
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+function Select({
+ ...props
+}: React.ComponentProps
) {
+ return
+}
+
+function SelectGroup({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectValue({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectTrigger({
+ className,
+ size = "default",
+ children,
+ ...props
+}: React.ComponentProps & {
+ size?: "sm" | "default"
+}) {
+ return (
+
+ {children}
+
+
+
+
+ )
+}
+
+function SelectContent({
+ className,
+ children,
+ position = "item-aligned",
+ align = "center",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ )
+}
+
+function SelectLabel({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SelectItem({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function SelectSeparator({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SelectScrollUpButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function SelectScrollDownButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+export {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
+}
diff --git a/packages/frontend/src/components/ui/table.tsx b/packages/frontend/src/components/ui/table.tsx
new file mode 100644
index 0000000..5513a5c
--- /dev/null
+++ b/packages/frontend/src/components/ui/table.tsx
@@ -0,0 +1,114 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Table({ className, ...props }: React.ComponentProps<"table">) {
+ return (
+
+ )
+}
+
+function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
+ return (
+
+ )
+}
+
+function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
+ return (
+
+ )
+}
+
+function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
+ return (
+ tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
+ return (
+
+ )
+}
+
+function TableHead({ className, ...props }: React.ComponentProps<"th">) {
+ return (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCell({ className, ...props }: React.ComponentProps<"td">) {
+ return (
+ | [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCaption({
+ className,
+ ...props
+}: React.ComponentProps<"caption">) {
+ return (
+
+ )
+}
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
|