Some frontend improvements / bd testing was cpmpleted

This commit is contained in:
Max 2024-10-20 21:00:35 +03:00
parent 6b4fe31d67
commit ae9bd6f9d6
13 changed files with 190 additions and 63 deletions

View File

@ -1,53 +1,20 @@
import "./App.css"; import "./App.css";
import "@radix-ui/themes/styles.css"; import "@radix-ui/themes/styles.css";
import { import {
Badge,
Button,
Callout,
Container,
Flex,
Separator,
Text,
Theme, Theme,
ThemePanel, ThemePanel,
} from "@radix-ui/themes"; } from "@radix-ui/themes";
import { import {
Router,
Route,
createBrowserRouter, createBrowserRouter,
createRoutesFromElements,
RouterProvider, RouterProvider,
Routes,
useRouteError,
} from "react-router-dom"; } from "react-router-dom";
import MainPage from "./Pages/MainPage/MainPage";
import { QueryClientProvider } from "@tanstack/react-query"; import { QueryClientProvider } from "@tanstack/react-query";
import queryClient from "./api/QueryClient/QueryClient"; import queryClient from "./api/QueryClient/QueryClient";
import { routes } from "./routes/routes";
function ErrorBoundary() {
let error = useRouteError();
console.error(error);
return <div>Dang! This route does not exist... Yet ;)</div>;
}
const router = createBrowserRouter( const router = createBrowserRouter(
createRoutesFromElements( routes
<>
<Route
path="/"
errorElement={<ErrorBoundary />}
element={<MainPage />}
>
<Route index element={<Text>Cringer path</Text>} />
<Route
path="/a?/c"
element={<Text>Cringer path, but this a</Text>}
></Route>
</Route>
</>
)
); );
export default function App() { export default function App() {

View File

@ -1,4 +1,4 @@
import { Button, Text } from "@radix-ui/themes"; import { Button, Card, ChevronDownIcon, Text } from "@radix-ui/themes";
import * as NavigationMenu from "@radix-ui/react-navigation-menu"; import * as NavigationMenu from "@radix-ui/react-navigation-menu";
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation, useNavigate } from "react-router-dom";
@ -10,10 +10,31 @@ export default function NavBar() {
className="flex justify-center" className="flex justify-center"
> >
<NavigationMenu.List className="flex justify-center gap-2"> <NavigationMenu.List className="flex justify-center gap-2">
<NavItem text="Cringer" to="/"/> <NavItem text="Cringer" to="/" />
<NavItem text="C-Cringer" to="/c"/> <NavItem text="C-Cringer" to="/c" />
<NavigationMenu.Item className="text-center">
<NavigationMenu.Trigger className="flex items-center">
<Button
asChild
className="w-fit h-fit rounded-full m-0 p-0 pr-2 pl-2 mt-2 mb-2 duration-[50ms]"
variant="ghost"
highContrast
>
<Text
size={"3"}
className="flex items-center gap-1"
>
Cringer 123 <ChevronDownIcon />
</Text>
</Button>
</NavigationMenu.Trigger>
<NavigationMenu.Content className="absolute data-[motion=from-start]:scale-150">
<Card>asd</Card>
</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List> </NavigationMenu.List>
</NavigationMenu.Root> </NavigationMenu.Root>
</nav> </nav>
@ -26,26 +47,20 @@ type TNavItem = {
}; };
function NavItem(props: TNavItem) { function NavItem(props: TNavItem) {
const navigate = useNavigate();
const navigate = useNavigate() const location = useLocation();
const location = useLocation()
console.log(location);
return ( return (
<NavigationMenu.Item> <NavigationMenu.Item>
<NavigationMenu.Link> <NavigationMenu.Link>
<Button <Button
className="w-fit h-fit rounded-full m-0 p-0 pr-2 pl-2 mt-2 mb-2" className="w-fit h-fit rounded-full m-0 p-0 pr-2 pl-2 mt-2 mb-2 duration-[50ms]"
highContrast
highContrast variant={location.pathname === props.to ? "solid" : "ghost"}
onClick={() => navigate(props.to)}
variant={location.pathname === props.to ? 'solid' : 'ghost'} >
onClick={() => navigate(props.to)} <Text size={"3"}>{props.text}</Text>
> </Button>
<Text size={"3"}>{props.text}</Text>
</Button>
</NavigationMenu.Link> </NavigationMenu.Link>
</NavigationMenu.Item> </NavigationMenu.Item>
); );

View File

@ -0,0 +1,27 @@
import { createRoutesFromElements, Route, useRouteError } from "react-router-dom"
import MainPage from "../Pages/MainPage/MainPage"
import {Text} from "@radix-ui/themes";
function ErrorBoundary() {
let error = useRouteError();
console.error(error);
return <div>Dang! This route does not exist... Yet ;)</div>;
}
export const routes = createRoutesFromElements(
<>
<Route
path="/"
errorElement={<ErrorBoundary />}
element={<MainPage />}
>
<Route index element={<Text>Cringer path</Text>} />
<Route
path="/a?/c"
element={<Text>Cringer path, but this a</Text>}
></Route>
</Route>
</>
)

View File

@ -5,7 +5,16 @@ content: [
"./src/**/*.{js,ts,jsx,tsx}", "./src/**/*.{js,ts,jsx,tsx}",
], ],
theme: { theme: {
extend: {}, extend: {
animation: {
'appear': 'appear 0.25s'
},
keyframes: {
appear: {
'100%': {opacity: '1'}
}
}
},
}, },
plugins: [], plugins: [],
} }

View File

@ -0,0 +1,44 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: favorites_queries.sql
package db_repo
import (
"context"
)
const createFavorite = `-- name: CreateFavorite :one
INSERT INTO public.favorites
(user_id, blog_id, favorited_at)
VALUES($1, $2, CURRENT_TIMESTAMP)
RETURNING user_id, blog_id, favorited_at
`
type CreateFavoriteParams struct {
UserID int64 `json:"user_id"`
BlogID int64 `json:"blog_id"`
}
func (q *Queries) CreateFavorite(ctx context.Context, arg CreateFavoriteParams) (Favorite, error) {
row := q.db.QueryRow(ctx, createFavorite, arg.UserID, arg.BlogID)
var i Favorite
err := row.Scan(&i.UserID, &i.BlogID, &i.FavoritedAt)
return i, err
}
const deleteFavorite = `-- name: DeleteFavorite :exec
DELETE FROM public.favorites
WHERE user_id=$1 AND blog_id=$2
`
type DeleteFavoriteParams struct {
UserID int64 `json:"user_id"`
BlogID int64 `json:"blog_id"`
}
func (q *Queries) DeleteFavorite(ctx context.Context, arg DeleteFavoriteParams) error {
_, err := q.db.Exec(ctx, deleteFavorite, arg.UserID, arg.BlogID)
return err
}

View File

@ -0,0 +1,48 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: multi_queries.sql
package db_repo
import (
"context"
)
const getFavoriteBlogsInfosByUserId = `-- name: GetFavoriteBlogsInfosByUserId :many
SELECT blogs.blog_id, blogs.user_id, blogs.title, blogs.description, blogs.category_id, blogs.created_at
FROM favorites
JOIN blogs on blogs.blog_id = favorites.blog_id
WHERE favorites.user_id = $1
`
type GetFavoriteBlogsInfosByUserIdRow struct {
Blog Blog `json:"blog"`
}
func (q *Queries) GetFavoriteBlogsInfosByUserId(ctx context.Context, userID int64) ([]GetFavoriteBlogsInfosByUserIdRow, error) {
rows, err := q.db.Query(ctx, getFavoriteBlogsInfosByUserId, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetFavoriteBlogsInfosByUserIdRow
for rows.Next() {
var i GetFavoriteBlogsInfosByUserIdRow
if err := rows.Scan(
&i.Blog.BlogID,
&i.Blog.UserID,
&i.Blog.Title,
&i.Blog.Description,
&i.Blog.CategoryID,
&i.Blog.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@ -25,4 +25,6 @@ CREATE TABLE "public"."post_tags" ("post_id" bigint NOT NULL, "tag_id" integer N
-- Create "post_votes" table -- Create "post_votes" table
CREATE TABLE "public"."post_votes" ("post_id" bigint NOT NULL, "user_id" bigint NOT NULL, "vote" boolean NOT NULL, PRIMARY KEY ("post_id", "user_id"), CONSTRAINT "post_votes_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "post_votes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE NO ACTION); CREATE TABLE "public"."post_votes" ("post_id" bigint NOT NULL, "user_id" bigint NOT NULL, "vote" boolean NOT NULL, PRIMARY KEY ("post_id", "user_id"), CONSTRAINT "post_votes_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "post_votes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE NO ACTION);
-- Create "profiles" table -- Create "profiles" table
CREATE TABLE "public"."profiles" ("profile_id" bigint NOT NULL, "user_id" bigint NULL, "bio" text NULL, "avatar_url" character varying(255) NULL, "website_url" character varying(100) NULL, PRIMARY KEY ("profile_id"), CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE); CREATE TABLE "public"."profiles" ("profile_id" bigint NOT NULL, "user_id" bigint NOT NULL, "bio" text NULL, "avatar_url" character varying(255) NULL, "website_url" character varying(100) NULL, PRIMARY KEY ("profile_id"), CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
-- Create index "profiles_user_id_idx" to table: "profiles"
CREATE UNIQUE INDEX "profiles_user_id_idx" ON "public"."profiles" ("user_id");

View File

@ -0,0 +1,9 @@
-- name: CreateFavorite :one
INSERT INTO public.favorites
(user_id, blog_id, favorited_at)
VALUES($1, $2, CURRENT_TIMESTAMP)
RETURNING *;
-- name: DeleteFavorite :exec
DELETE FROM public.favorites
WHERE user_id=$1 AND blog_id=$2;

View File

@ -0,0 +1,6 @@
-- name: GetFavoriteBlogsInfosByUserId :many
SELECT sqlc.embed(blogs)
FROM favorites
JOIN blogs on blogs.blog_id = favorites.blog_id
WHERE favorites.user_id = $1;

View File

@ -30,7 +30,7 @@ func main() {
} }
// Transaction // Transaction
tx, _ := utils.Dbx_connection.Begin(context.Background()) tx, _ := utils.Dbx.Begin(context.Background())
defer tx.Rollback(context.Background()) defer tx.Rollback(context.Background())
repo := db_repo.New(tx) repo := db_repo.New(tx)

View File

@ -20,9 +20,9 @@ type Argon2Hash struct {
} }
type HashSalt struct { type HashSalt struct {
hash []byte Hash []byte
salt []byte salt []byte
stringToStore string StringToStore string
} }
// Initializer for algorithm // Initializer for algorithm
@ -75,7 +75,7 @@ func (a *Argon2Hash) HashGen(password, salt []byte) (*HashSalt, error) {
stringToStore := fmt.Sprintf("$m=%d,t=%d$%s$%s", a.memory, a.time, saltDecoded, hashDecoded) stringToStore := fmt.Sprintf("$m=%d,t=%d$%s$%s", a.memory, a.time, saltDecoded, hashDecoded)
// This is unnecessary structure i created following the guide 0_0 // This is unnecessary structure i created following the guide 0_0
return &HashSalt{hash: hash, salt: salt, stringToStore: stringToStore}, nil return &HashSalt{Hash: hash, salt: salt, StringToStore: stringToStore}, nil
} }
@ -91,7 +91,7 @@ func (a *Argon2Hash) Compare(hash, salt, password []byte) error {
} }
// Comparing hashes // Comparing hashes
if !bytes.Equal(hash, hashSalt.hash) { if !bytes.Equal(hash, hashSalt.Hash) {
return fmt.Errorf("invalid password (hashes does not match)") return fmt.Errorf("invalid password (hashes does not match)")
} }
@ -153,6 +153,6 @@ func Test() {
} }
// fmt.Println(testDbString) // fmt.Println(testDbString)
fmt.Printf("%s", cringe.stringToStore) fmt.Printf("%s", cringe.StringToStore)
fmt.Print("\n\n\n\n") fmt.Print("\n\n\n\n")
} }

View File

@ -206,7 +206,7 @@ func registerUser(c *gin.Context) {
rowsToClose, errr := Dbx.Query(context.Background(), "INSERT INTO users "+ rowsToClose, errr := Dbx.Query(context.Background(), "INSERT INTO users "+
"(user_id, username, user_name, user_password) "+ "(user_id, username, user_name, user_password) "+
"VALUES($1, $2, $3, $4);", newUuid, body.Username, body.Name, hashedPassword.stringToStore) "VALUES($1, $2, $3, $4);", newUuid, body.Username, body.Name, hashedPassword.StringToStore)
if errr != nil { if errr != nil {
c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": errr.Error()}) c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": errr.Error()})