Main page / CORS
This commit is contained in:
@@ -1,10 +1,20 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from db.users import get_users, get_attendance
|
from db.users import get_users, get_attendance
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
# Get fastapi app
|
# Get fastapi app
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
# Add cors headers to the app
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["GET"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
def list_users():
|
def list_users():
|
||||||
return get_users()
|
return get_users()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<title>Bolderjara Serviss</title>
|
<title>Bolderjara Serviss</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-900">
|
<body class="bg-body">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Link } from "@tanstack/react-router";
|
|||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return (
|
return (
|
||||||
<header className="flex gap-4 p-4 bg-gray-800 font-bold text-gray-100">
|
<header className="flex gap-4 p-4 bg-panel shadow font-bold">
|
||||||
<nav className="">
|
<nav className="text-accent">
|
||||||
<Link to="/">Home</Link>
|
<Link to="/">Home</Link>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export default function MainContainer(props: { children: React.ReactNode; className?: string }) {
|
export default function MainContainer(props: { children: React.ReactNode; className?: string }) {
|
||||||
return <main className={`max-w-4xl mx-auto ${props.className || ""}`}>{props.children}</main>;
|
return <main className={`max-w-4xl px-4 mx-auto ${props.className || ""}`}>{props.children}</main>;
|
||||||
}
|
}
|
||||||
|
|||||||
53
frontend/src/components/UsersTable.tsx
Normal file
53
frontend/src/components/UsersTable.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { API_BASE_URL } from "@/consts";
|
||||||
|
import type { User } from "@/types/api";
|
||||||
|
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
function RenderList({ list }: { list: UseQueryResult<User[]> }) {
|
||||||
|
// Base classes for each row
|
||||||
|
const baseRowClass = "text-xl p-2 px-4 rounded capitalize bg-panel/75";
|
||||||
|
|
||||||
|
// Display loading and error states
|
||||||
|
if (list.isLoading) return <div className={`${baseRowClass} text-gray-400`}>Loading...</div>;
|
||||||
|
if (list.isError) return <div className={`${baseRowClass} text-red-400`}>Error: {list.error.message}</div>;
|
||||||
|
|
||||||
|
// Render all users to the list
|
||||||
|
return list.data?.map((u) => {
|
||||||
|
return (
|
||||||
|
<p className={`${baseRowClass} transition cursor-pointer hover:bg-panel hover:text-accent`}>{u.username}</p>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UsersTable() {
|
||||||
|
// UseState for search input
|
||||||
|
const [search, setSearch] = useState(""); // For input control
|
||||||
|
const [debouncedSearch, setDebouncedSearch] = useState(search); // For when user stops typing, do refetching
|
||||||
|
|
||||||
|
// Build search query url
|
||||||
|
const fetchUsersURL = new URL(`${API_BASE_URL}/`);
|
||||||
|
if (search) fetchUsersURL.searchParams.set("s", search);
|
||||||
|
|
||||||
|
// Using tanstack query to fetch, and automatically cache the data
|
||||||
|
const fetchUsers = () => fetch(fetchUsersURL).then((res) => res.json());
|
||||||
|
const users = useQuery<User[]>({ queryKey: ["users", debouncedSearch], queryFn: fetchUsers });
|
||||||
|
|
||||||
|
// Use effect is being used to make refetch after user has stopped typing in the search bar
|
||||||
|
useEffect(() => {
|
||||||
|
const timeout = setTimeout(() => setDebouncedSearch(search), 500);
|
||||||
|
return () => clearTimeout(timeout); // Clear timeout
|
||||||
|
}, [search]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
placeholder="Search users"
|
||||||
|
className="bg-panel/75 transition focus:bg-panel border border-transparent outline-none focus:border-accent rounded px-4 py-2 mt-8 w-full "
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
/>
|
||||||
|
<ul className="space-y-2 mt-2">
|
||||||
|
<RenderList list={users} />
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
frontend/src/consts.ts
Normal file
1
frontend/src/consts.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const API_BASE_URL = "http://127.0.0.1:8000";
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import MainContainer from "@/components/MainContainer";
|
import MainContainer from "@/components/MainContainer";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import UsersTable from "@/components/UsersTable";
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
component: App,
|
component: App,
|
||||||
@@ -8,7 +9,8 @@ export const Route = createFileRoute("/")({
|
|||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<MainContainer className="pt-8">
|
<MainContainer className="pt-8">
|
||||||
<h1 className="text-4xl font-bold text-center text-gray-100">Darbinieki</h1>
|
<h1 className="text-4xl font-bold text-center text-accent">Darbinieki</h1>
|
||||||
|
<UsersTable />
|
||||||
</MainContainer>
|
</MainContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-accent: #123ffb;
|
||||||
|
--color-body: #dfe1e5;
|
||||||
|
--color-panel: white;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply m-0;
|
@apply m-0;
|
||||||
font-family:
|
font-family:
|
||||||
|
|||||||
3
frontend/src/types/api.ts
Normal file
3
frontend/src/types/api.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface User {
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user