mirror of
https://github.com/Steffo99/todocolors.git
synced 2024-11-22 08:14:18 +00:00
Make some frontend progress
This commit is contained in:
parent
88b3ab1674
commit
3c6b9ba36e
19 changed files with 473 additions and 111 deletions
|
@ -17,6 +17,7 @@
|
||||||
"@types/node": "20.4.5",
|
"@types/node": "20.4.5",
|
||||||
"@types/react": "18.2.17",
|
"@types/react": "18.2.17",
|
||||||
"@types/react-dom": "18.2.7",
|
"@types/react-dom": "18.2.7",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
"next": "13.4.12",
|
"next": "13.4.12",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
|
38
todoblue/src/app/CreatePrivateBoard.tsx
Normal file
38
todoblue/src/app/CreatePrivateBoard.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {useBoardCreator} from "@/app/useBoardCreator"
|
||||||
|
import {faKey} from "@fortawesome/free-solid-svg-icons"
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
|
import {default as React} from "react"
|
||||||
|
|
||||||
|
export function CreatePrivateBoard() {
|
||||||
|
const {createBoard} = useBoardCreator();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className={"panel box form-flex"}
|
||||||
|
onSubmit={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
createBoard(crypto.randomUUID().toString());
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3>
|
||||||
|
<FontAwesomeIcon icon={faKey} size={"1x"}/>
|
||||||
|
{" "}
|
||||||
|
Privato
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Crea un nuovo tabellone privato utilizzando un codice segreto autogenerato!
|
||||||
|
<br/>
|
||||||
|
<small>Esso sarà accessibile solo da chi ne conosce il link.</small>
|
||||||
|
</p>
|
||||||
|
<label className={"float-bottom"}>
|
||||||
|
<span/>
|
||||||
|
<button>
|
||||||
|
Crea
|
||||||
|
</button>
|
||||||
|
<span/>
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
57
todoblue/src/app/CreatePublicBoard.tsx
Normal file
57
todoblue/src/app/CreatePublicBoard.tsx
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import {useBoardCreator} from "@/app/useBoardCreator"
|
||||||
|
import {useLowerKebabState} from "@/app/useKebabState"
|
||||||
|
import {faGlobe} from "@fortawesome/free-solid-svg-icons"
|
||||||
|
import {default as React} from "react"
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
|
|
||||||
|
|
||||||
|
export function CreatePublicBoard() {
|
||||||
|
const [code, setCode] = useLowerKebabState("")
|
||||||
|
const {createBoard} = useBoardCreator();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className={"panel box form-flex"}
|
||||||
|
onSubmit={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
createBoard(code);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3>
|
||||||
|
<FontAwesomeIcon icon={faGlobe}/>
|
||||||
|
{" "}
|
||||||
|
Pubblico
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Crea un nuovo tabellone pubblico, con un codice personalizzato!
|
||||||
|
<br/>
|
||||||
|
<small>Se un tabellone con quel codice esiste già, sarai reindirizzato ad esso.</small>
|
||||||
|
</p>
|
||||||
|
<label className={"float-bottom"}>
|
||||||
|
<span>
|
||||||
|
Codice
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type={"text"}
|
||||||
|
placeholder={"garasauto-planning-2023"}
|
||||||
|
value={code}
|
||||||
|
onChange={(
|
||||||
|
e => setCode(e.target.value)
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<span/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span/>
|
||||||
|
<button
|
||||||
|
onClick={_ => createBoard(code)}
|
||||||
|
>
|
||||||
|
Crea
|
||||||
|
</button>
|
||||||
|
<span/>
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
65
todoblue/src/app/board/[board]/page.module.css
Normal file
65
todoblue/src/app/board/[board]/page.module.css
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
.boardHeader {
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 450px) {
|
||||||
|
.boardHeader {
|
||||||
|
grid-template-areas:
|
||||||
|
"left right"
|
||||||
|
"title title"
|
||||||
|
;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 451px) {
|
||||||
|
.boardHeader {
|
||||||
|
grid-template-areas:
|
||||||
|
"left title right"
|
||||||
|
;
|
||||||
|
grid-template-columns: auto 1fr auto;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardButtons {
|
||||||
|
width: auto;
|
||||||
|
min-width: unset;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardButtonsLeft {
|
||||||
|
grid-area: left;
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardButtonsRight {
|
||||||
|
grid-area: right;
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardTitle {
|
||||||
|
grid-area: title;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boardButtons button {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background-color: hsla(
|
||||||
|
var(--bhsl-background-hue),
|
||||||
|
var(--bhsl-background-saturation),
|
||||||
|
var(--bhsl-background-lightness),
|
||||||
|
100%
|
||||||
|
);
|
||||||
|
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
|
@ -1,15 +1,73 @@
|
||||||
'use client';
|
"use client";
|
||||||
import {default as React} from "react";
|
|
||||||
import {useBoard} from "@/app/board/[board]/useBoard"
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
|
import {useEffect} from "react"
|
||||||
|
import {useBoardWebSocket} from "@/app/board/[board]/useBoardWebSocket"
|
||||||
|
import style from "./page.module.css";
|
||||||
|
import classNames from "classnames"
|
||||||
|
import {faHouse, faPencil, faTableColumns, faArrowDownWideShort} from "@fortawesome/free-solid-svg-icons"
|
||||||
|
|
||||||
|
|
||||||
export default function Page({params: {board}}: {params: {board: string}}) {
|
export default function Page({params: {board}}: {params: {board: string}}) {
|
||||||
const wsUrl = React.useMemo(() => `ws://127.0.0.1:8080/board/${board}/ws`, [board])
|
const {tasks, title, pushEvent, readyState} = useBoardWebSocket(board);
|
||||||
useBoard(wsUrl);
|
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
<p>
|
console.debug("[Page] Current events: ", tasks)
|
||||||
TODO
|
}, [tasks])
|
||||||
</p>
|
|
||||||
)
|
return <>
|
||||||
|
<header className={style.boardHeader}>
|
||||||
|
<div className={classNames(style.boardButtons, style.boardButtonsLeft)}>
|
||||||
|
<button title={"Home"}>
|
||||||
|
<FontAwesomeIcon icon={faHouse}/>
|
||||||
|
</button>
|
||||||
|
<button title={"Modifica titolo"}>
|
||||||
|
<FontAwesomeIcon icon={faPencil}/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h1 className={style.boardTitle}>
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
<div className={classNames(style.boardButtons, style.boardButtonsRight)}>
|
||||||
|
<button title={"Cambia raggruppamento orizzontale"}>
|
||||||
|
<FontAwesomeIcon icon={faTableColumns}/>
|
||||||
|
</button>
|
||||||
|
<button title={"Cambia ordinamento verticale"}>
|
||||||
|
<FontAwesomeIcon icon={faArrowDownWideShort}/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<div className={"chapter-5"}>
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
Gruppo A
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
Gruppo B
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
Gruppo C
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
Gruppo D
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
Gruppo E
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
sos
|
||||||
|
</footer>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
|
|
62
todoblue/src/app/board/[board]/types.ts
Normal file
62
todoblue/src/app/board/[board]/types.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
export type TaskIcon =
|
||||||
|
"User" &
|
||||||
|
"Image" &
|
||||||
|
"Envelope" &
|
||||||
|
"Star" &
|
||||||
|
"Heart" &
|
||||||
|
"Comment" &
|
||||||
|
"FaceSmile" &
|
||||||
|
"File" &
|
||||||
|
"Bell" &
|
||||||
|
"Bookmark" &
|
||||||
|
"Eye" &
|
||||||
|
"Hand" &
|
||||||
|
"PaperPlane" &
|
||||||
|
"Handshake" &
|
||||||
|
"Sun" &
|
||||||
|
"Clock" &
|
||||||
|
"Circle" &
|
||||||
|
"Square" &
|
||||||
|
"Building" &
|
||||||
|
"Flag" &
|
||||||
|
"Moon";
|
||||||
|
|
||||||
|
export type TaskImportance =
|
||||||
|
"Highest" &
|
||||||
|
"High" &
|
||||||
|
"Normal" &
|
||||||
|
"Low" &
|
||||||
|
"Lowest";
|
||||||
|
|
||||||
|
export type TaskPriority =
|
||||||
|
"Highest" &
|
||||||
|
"High" &
|
||||||
|
"Normal" &
|
||||||
|
"Low" &
|
||||||
|
"Lowest";
|
||||||
|
|
||||||
|
export type TaskStatus =
|
||||||
|
"Unfinished" &
|
||||||
|
"InProgress" &
|
||||||
|
"Complete"
|
||||||
|
|
||||||
|
export type Task = {
|
||||||
|
text: string,
|
||||||
|
icon: TaskIcon,
|
||||||
|
importance: TaskImportance,
|
||||||
|
priority: TaskPriority,
|
||||||
|
status: TaskStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TitleBoardAction = {
|
||||||
|
"Title": string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TaskBoardAction = {
|
||||||
|
"Task": [
|
||||||
|
string,
|
||||||
|
Task,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BoardAction = TitleBoardAction & TaskBoardAction;
|
|
@ -1,18 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import {default as React} from "react";
|
|
||||||
import {useWs} from "@/app/board/[board]/useWs"
|
|
||||||
|
|
||||||
|
|
||||||
export function useBoard(url: string) {
|
|
||||||
const socket = useWs(url, {
|
|
||||||
onopen: React.useCallback((sock: WebSocket, event: Event) => {
|
|
||||||
console.debug("[useBoard] Connected to the server!");
|
|
||||||
sock.send('{"Title": "sus"}')
|
|
||||||
}, []),
|
|
||||||
onmessage: React.useCallback((sock: WebSocket, event: MessageEvent) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
console.debug("[useBoard] Received ServerOperation: ", data);
|
|
||||||
}, [])
|
|
||||||
});
|
|
||||||
}
|
|
57
todoblue/src/app/board/[board]/useBoardWebSocket.ts
Normal file
57
todoblue/src/app/board/[board]/useBoardWebSocket.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import {BoardAction, Task} from "@/app/board/[board]/types"
|
||||||
|
import {useMemo, useCallback, useState} from "react"
|
||||||
|
import {useWebSocket} from "@/app/board/[board]/useWebSocket"
|
||||||
|
|
||||||
|
|
||||||
|
export function useBoardWebSocket(board: string) {
|
||||||
|
const url = useMemo(() => `ws://127.0.0.1:8080/board/${board}/ws`, [board]);
|
||||||
|
const [title, setTitle] = useState<string>("Nuovo tabellone");
|
||||||
|
const [tasks, setTasks] = useState<{[key: string]: Task}>(() => ({}));
|
||||||
|
|
||||||
|
const onopen = useCallback((sock: WebSocket, event: Event) => {
|
||||||
|
setTasks(() => ({}))
|
||||||
|
console.debug("[useBoardWebSocket] Connected to the websocket of board:", board);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onmessage = useCallback((sock: WebSocket, event: MessageEvent) => {
|
||||||
|
const data: BoardAction = JSON.parse(event.data);
|
||||||
|
console.debug("[useBoardWebSocket] Received:", data);
|
||||||
|
if(data["Title"] !== undefined) {
|
||||||
|
setTitle(data["Title"]);
|
||||||
|
}
|
||||||
|
else if(data["Task"] !== undefined) {
|
||||||
|
const id = data["Task"][0]
|
||||||
|
const task = data["Task"][1]
|
||||||
|
setTasks((prevTasks) => {
|
||||||
|
const tasks = {...prevTasks}
|
||||||
|
if(task === null) {
|
||||||
|
delete tasks[id]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tasks[id] = task
|
||||||
|
}
|
||||||
|
return tasks
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const {websocket} = useWebSocket(url, {onopen, onmessage});
|
||||||
|
const readyState = websocket?.readyState;
|
||||||
|
|
||||||
|
const pushEvent = useCallback((data: any) => {
|
||||||
|
if(!websocket) {
|
||||||
|
console.warn("[useBoardWebSocket] Socket does not exist yet, cannot send:", data)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(readyState != 1) {
|
||||||
|
console.warn("[useBoardWebSocket] Socket isn't ready yet, cannot send:", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.debug("[useBoardWebSocket] Sending:", data);
|
||||||
|
websocket.send(JSON.stringify(data));
|
||||||
|
}, [websocket, readyState])
|
||||||
|
|
||||||
|
return {title, tasks, pushEvent, readyState}
|
||||||
|
}
|
43
todoblue/src/app/board/[board]/useWebSocket.ts
Normal file
43
todoblue/src/app/board/[board]/useWebSocket.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import {useState, useEffect, useCallback} from "react"
|
||||||
|
|
||||||
|
export interface WebSocketHandlers {
|
||||||
|
onclose?: (sock: WebSocket, event: CloseEvent) => void,
|
||||||
|
onerror?: (sock: WebSocket, event: Event) => void,
|
||||||
|
onmessage?: (sock: WebSocket, event: MessageEvent) => void,
|
||||||
|
onopen?: (sock: WebSocket, event: Event) => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useWebSocket(url: string, {onclose, onerror, onmessage, onopen}: WebSocketHandlers) {
|
||||||
|
const [websocket, setWebsocket] = useState<WebSocket | null>(null)
|
||||||
|
const [readyState, setReadyState] = useState<number>(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.debug("[useWebSocket] Creating websocket...");
|
||||||
|
const sock = new WebSocket(url);
|
||||||
|
setWebsocket(sock);
|
||||||
|
sock.onopen = (ev) => {
|
||||||
|
setReadyState(sock.readyState);
|
||||||
|
onopen?.(sock, ev);
|
||||||
|
}
|
||||||
|
sock.onclose = (ev) => {
|
||||||
|
setReadyState(sock.readyState);
|
||||||
|
onclose?.(sock, ev);
|
||||||
|
}
|
||||||
|
sock.onerror = (ev) => {
|
||||||
|
setReadyState(sock.readyState);
|
||||||
|
onerror?.(sock, ev);
|
||||||
|
}
|
||||||
|
sock.onmessage = (ev) => {
|
||||||
|
onmessage?.(sock, ev);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
console.debug("[useWebSocket] Closing websocket...");
|
||||||
|
sock.close();
|
||||||
|
setWebsocket(null);
|
||||||
|
}
|
||||||
|
}, [url, onclose, onerror, onmessage, onopen])
|
||||||
|
|
||||||
|
return {websocket, readyState}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
'use client';
|
|
||||||
import {default as React} from "react";
|
|
||||||
|
|
||||||
|
|
||||||
export interface UseWsHandlers {
|
|
||||||
onclose?: (sock: WebSocket, event: CloseEvent) => void,
|
|
||||||
onerror?: (sock: WebSocket, event: Event) => void,
|
|
||||||
onmessage?: (sock: WebSocket, event: MessageEvent) => void,
|
|
||||||
onopen?: (sock: WebSocket, event: Event) => void,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function useWs(url: string, {onclose, onerror, onmessage, onopen}: UseWsHandlers) {
|
|
||||||
const [websocket, setWebsocket] = React.useState<WebSocket | null>(null)
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const sock = new WebSocket(url);
|
|
||||||
sock.onclose = onclose ? (ev) => onclose(sock, ev) : null;
|
|
||||||
sock.onerror = onerror ? (ev) => onerror(sock, ev) : null;
|
|
||||||
sock.onmessage = onmessage ? (ev) => onmessage(sock, ev) : null;
|
|
||||||
sock.onopen = onopen ? (ev) => onopen(sock, ev) : null;
|
|
||||||
setWebsocket(sock);
|
|
||||||
return () => {
|
|
||||||
sock.close();
|
|
||||||
setWebsocket(null);
|
|
||||||
}
|
|
||||||
}, [url, onclose, onerror, onmessage, onopen])
|
|
||||||
|
|
||||||
return websocket
|
|
||||||
}
|
|
|
@ -6,9 +6,13 @@ import "@steffo/bluelib/dist/glass.root.css"
|
||||||
import "@steffo/bluelib/dist/layouts-center.root.css"
|
import "@steffo/bluelib/dist/layouts-center.root.css"
|
||||||
import "@steffo/bluelib/dist/colors-royalblue.root.css"
|
import "@steffo/bluelib/dist/colors-royalblue.root.css"
|
||||||
import "@steffo/bluelib/dist/fonts-fira-ghpages.root.css"
|
import "@steffo/bluelib/dist/fonts-fira-ghpages.root.css"
|
||||||
|
import '@fortawesome/fontawesome-svg-core/styles.css';
|
||||||
|
|
||||||
|
import { config } from '@fortawesome/fontawesome-svg-core';
|
||||||
|
config.autoAddCss = false; /* eslint-disable import/first */
|
||||||
|
|
||||||
import type {Metadata as NextMetadata} from "next"
|
import type {Metadata as NextMetadata} from "next"
|
||||||
import {ReactNode} from "react"
|
import {default as React, ReactNode} from "react"
|
||||||
|
|
||||||
|
|
||||||
export const metadata: NextMetadata = {
|
export const metadata: NextMetadata = {
|
||||||
|
@ -21,6 +25,13 @@ export default function RootLayout({children}: { children: ReactNode }) {
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={"theme-bluelib layout-center"}>
|
<body className={"theme-bluelib layout-center"}>
|
||||||
{children}
|
{children}
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
© <a href="https://steffo.eu">Stefano Pigozzi</a> -
|
||||||
|
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html">AGPL 3.0</a> -
|
||||||
|
<a href="https://github.com/Steffo99/todocolors">GitHub</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import {CreatePrivateBoard} from "@/app/CreatePrivateBoard"
|
||||||
|
import {CreatePublicBoard} from "@/app/CreatePublicBoard"
|
||||||
import {default as React} from "react";
|
import {default as React} from "react";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
@ -12,55 +14,9 @@ export default function Page() {
|
||||||
<h2>
|
<h2>
|
||||||
Crea un nuovo tabellone
|
Crea un nuovo tabellone
|
||||||
</h2>
|
</h2>
|
||||||
<form className={"panel box form-flex"}>
|
<CreatePublicBoard/>
|
||||||
<h3>
|
<CreatePrivateBoard/>
|
||||||
Pubblico
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Crea un nuovo tabellone pubblico, con un codice personalizzato!
|
|
||||||
<br/>
|
|
||||||
<small>Se un tabellone con quel codice esiste già, sarai reindirizzato ad esso.</small>
|
|
||||||
</p>
|
|
||||||
<label className={"float-bottom"}>
|
|
||||||
<span>
|
|
||||||
Codice
|
|
||||||
</span>
|
|
||||||
<input type={"text"} placeholder={"garas-auto-planning-2023"}/>
|
|
||||||
<span/>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<span/>
|
|
||||||
<button>
|
|
||||||
Crea
|
|
||||||
</button>
|
|
||||||
<span/>
|
|
||||||
</label>
|
|
||||||
</form>
|
|
||||||
<div className={"panel box form-flex"}>
|
|
||||||
<h3>
|
|
||||||
Privato
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
Crea un nuovo tabellone privato utilizzando un codice segreto autogenerato!
|
|
||||||
<br/>
|
|
||||||
<small>Esso sarà accessibile solo da chi ne conosce il link.</small>
|
|
||||||
</p>
|
|
||||||
<label className={"float-bottom"}>
|
|
||||||
<span/>
|
|
||||||
<button>
|
|
||||||
Crea
|
|
||||||
</button>
|
|
||||||
<span/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
© <a href="https://steffo.eu">Stefano Pigozzi</a> -
|
|
||||||
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html">AGPL 3.0</a> -
|
|
||||||
<a href="https://github.com/Steffo99/todocolors">GitHub</a>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
13
todoblue/src/app/useBoardCreator.ts
Normal file
13
todoblue/src/app/useBoardCreator.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
"use client";
|
||||||
|
import {useCallback} from "react"
|
||||||
|
import {useRouter} from "next/navigation"
|
||||||
|
|
||||||
|
export function useBoardCreator() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const createBoard = useCallback((board: String) => {
|
||||||
|
router.push(`/board/${board}`);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return {createBoard}
|
||||||
|
}
|
38
todoblue/src/app/useKebabState.ts
Normal file
38
todoblue/src/app/useKebabState.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {useCallback, useState} from "react"
|
||||||
|
|
||||||
|
const KEBABIFIER = /[^a-zA-Z0-9-]/g
|
||||||
|
|
||||||
|
export function useAnyKebabState(initial: string): [string, (inputString: string) => void] {
|
||||||
|
const [state, setInnerState] = useState<string>(initial);
|
||||||
|
|
||||||
|
const setState = useCallback((inputString: string) => {
|
||||||
|
const kebabifiedString = inputString.replaceAll(KEBABIFIER, "-");
|
||||||
|
setInnerState(kebabifiedString);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return [state, setState]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLowerKebabState(initial: string): [string, (inputString: string) => void] {
|
||||||
|
const [state, setInnerState] = useState<string>(initial);
|
||||||
|
|
||||||
|
const setState = useCallback((inputString: string) => {
|
||||||
|
const kebabifiedString = inputString.toLowerCase().replaceAll(KEBABIFIER, "-");
|
||||||
|
setInnerState(kebabifiedString);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return [state, setState]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUpperKebabState(initial: string): [string, (inputString: string) => void] {
|
||||||
|
const [state, setInnerState] = useState<string>(initial);
|
||||||
|
|
||||||
|
const setState = useCallback((inputString: string) => {
|
||||||
|
const kebabifiedString = inputString.toUpperCase().replaceAll(KEBABIFIER, "-");
|
||||||
|
setInnerState(kebabifiedString);
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return [state, setState]
|
||||||
|
}
|
|
@ -2,10 +2,7 @@
|
||||||
<module type="WEB_MODULE" version="4">
|
<module type="WEB_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/node_modules" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -140,6 +140,11 @@ caniuse-lite@^1.0.30001406:
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8"
|
||||||
integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==
|
integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==
|
||||||
|
|
||||||
|
classnames@^2.3.2:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
||||||
|
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
||||||
|
|
||||||
client-only@0.0.1:
|
client-only@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
||||||
|
|
2
todored/Cargo.lock
generated
2
todored/Cargo.lock
generated
|
@ -1071,11 +1071,13 @@ dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"deadqueue",
|
"deadqueue",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"micronfig",
|
"micronfig",
|
||||||
"pkg-version",
|
"pkg-version",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"redis",
|
"redis",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
@ -9,11 +9,13 @@ edition = "2021"
|
||||||
axum = { version = "0.6.19", features = ["ws"] }
|
axum = { version = "0.6.19", features = ["ws"] }
|
||||||
deadqueue = "0.2.4"
|
deadqueue = "0.2.4"
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.28"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.19"
|
log = "0.4.19"
|
||||||
micronfig = "0.2.0"
|
micronfig = "0.2.0"
|
||||||
pkg-version = "1.0.0"
|
pkg-version = "1.0.0"
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.5.0"
|
||||||
redis = { version = "0.23.1", features = ["r2d2", "ahash", "cluster", "tokio-comp", "connection-manager"] }
|
redis = { version = "0.23.1", features = ["r2d2", "ahash", "cluster", "tokio-comp", "connection-manager"] }
|
||||||
|
regex = "1.9.1"
|
||||||
serde = { version = "1.0.178", features = ["derive"] }
|
serde = { version = "1.0.178", features = ["derive"] }
|
||||||
serde_json = "1.0.104"
|
serde_json = "1.0.104"
|
||||||
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use axum::Extension;
|
use axum::Extension;
|
||||||
use axum::extract::{Path, WebSocketUpgrade};
|
use axum::extract::{Path, WebSocketUpgrade};
|
||||||
|
use uuid::Uuid;
|
||||||
use crate::kebab::Skewer;
|
use crate::kebab::Skewer;
|
||||||
|
use crate::routes::board::structs::{BoardAction, BoardRequest};
|
||||||
|
use crate::task::{Task, TaskIcon, TaskImportance, TaskPriority, TaskStatus};
|
||||||
use super::ws;
|
use super::ws;
|
||||||
|
|
||||||
pub(crate) async fn handler(
|
pub(crate) async fn handler(
|
||||||
|
@ -12,6 +15,8 @@ pub(crate) async fn handler(
|
||||||
let board = board.to_kebab_lowercase();
|
let board = board.to_kebab_lowercase();
|
||||||
log::trace!("Kebabified board name to: {board:?}");
|
log::trace!("Kebabified board name to: {board:?}");
|
||||||
|
|
||||||
|
log::info!("{}", serde_json::ser::to_string(&BoardAction::Task(Some(Uuid::new_v4()), Some(Task { text: "Hello world".to_string(), icon: TaskIcon::Bell, importance: TaskImportance::High, priority: TaskPriority::Highest, status: TaskStatus::Complete }))).unwrap());
|
||||||
|
|
||||||
log::trace!("Received websocket request, upgrading...");
|
log::trace!("Received websocket request, upgrading...");
|
||||||
upgrade_request.on_upgrade(|websocket| ws::handler(board, rclient, websocket))
|
upgrade_request.on_upgrade(|websocket| ws::handler(board, rclient, websocket))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue