mirror of
https://github.com/Steffo99/todocolors.git
synced 2024-11-22 16:24:19 +00:00
Final progress for today
This commit is contained in:
parent
0865ca94e7
commit
7a17afc267
15 changed files with 352 additions and 30 deletions
|
@ -7,7 +7,9 @@
|
||||||
</scripts>
|
</scripts>
|
||||||
<arguments value="--port=8081" />
|
<arguments value="--port=8081" />
|
||||||
<node-interpreter value="project" />
|
<node-interpreter value="project" />
|
||||||
<envs />
|
<envs>
|
||||||
|
<env name="NEXT_PUBLIC_API_BASE_URL" value="ws://192.168.1.135:8080" />
|
||||||
|
</envs>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
|
@ -10,7 +10,7 @@
|
||||||
<option name="buildTarget" value="REMOTE" />
|
<option name="buildTarget" value="REMOTE" />
|
||||||
<option name="backtrace" value="SHORT" />
|
<option name="backtrace" value="SHORT" />
|
||||||
<envs>
|
<envs>
|
||||||
<env name="AXUM_HOST" value="127.0.0.1:8080" />
|
<env name="AXUM_HOST" value="0.0.0.0:8080" />
|
||||||
<env name="REDIS_CONN" value="redis://127.0.0.1:6379/" />
|
<env name="REDIS_CONN" value="redis://127.0.0.1:6379/" />
|
||||||
<env name="RUST_LOG" value="todored" />
|
<env name="RUST_LOG" value="todored" />
|
||||||
</envs>
|
</envs>
|
||||||
|
|
|
@ -3,14 +3,22 @@
|
||||||
import {useBoardCreator} from "@/app/useBoardCreator"
|
import {useBoardCreator} from "@/app/useBoardCreator"
|
||||||
import {faKey} from "@fortawesome/free-solid-svg-icons"
|
import {faKey} from "@fortawesome/free-solid-svg-icons"
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
|
import classNames from "classnames"
|
||||||
import {default as React} from "react"
|
import {default as React} from "react"
|
||||||
|
|
||||||
export function CreatePrivateBoard() {
|
export function CreatePrivateBoard() {
|
||||||
const {createBoard} = useBoardCreator();
|
const {createBoard} = useBoardCreator();
|
||||||
|
|
||||||
|
const isSecure = typeof window !== "undefined" && window.isSecureContext;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className={"panel box form-flex"}
|
className={classNames({
|
||||||
|
"panel": true,
|
||||||
|
"box": true,
|
||||||
|
"form-flex": true,
|
||||||
|
"red": !isSecure,
|
||||||
|
})}
|
||||||
onSubmit={e => {
|
onSubmit={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
createBoard(crypto.randomUUID().toString());
|
createBoard(crypto.randomUUID().toString());
|
||||||
|
@ -21,6 +29,8 @@ export function CreatePrivateBoard() {
|
||||||
{" "}
|
{" "}
|
||||||
Privato
|
Privato
|
||||||
</h3>
|
</h3>
|
||||||
|
{isSecure ?
|
||||||
|
<>
|
||||||
<p>
|
<p>
|
||||||
Crea un nuovo tabellone privato utilizzando un codice segreto autogenerato!
|
Crea un nuovo tabellone privato utilizzando un codice segreto autogenerato!
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -33,6 +43,14 @@ export function CreatePrivateBoard() {
|
||||||
</button>
|
</button>
|
||||||
<span/>
|
<span/>
|
||||||
</label>
|
</label>
|
||||||
|
</> : <>
|
||||||
|
<p>
|
||||||
|
Questa funzionalità non è disponibile al di fuori di contesti sicuri.
|
||||||
|
<br/>
|
||||||
|
<small>Assicurati di stare usando HTTPS!</small>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import {BoardColumns} from "@/app/board/[board]/BoardColumns"
|
||||||
import {BoardError} from "@/app/board/[board]/BoardError"
|
import {BoardError} from "@/app/board/[board]/BoardError"
|
||||||
import {BoardLoading} from "@/app/board/[board]/BoardLoading"
|
import {BoardLoading} from "@/app/board/[board]/BoardLoading"
|
||||||
import {useBoardContext} from "@/app/board/[board]/useBoardContext"
|
import {useBoardContext} from "@/app/board/[board]/useBoardContext"
|
||||||
|
@ -12,7 +13,7 @@ export function BoardBody() {
|
||||||
case WebSocket.CONNECTING:
|
case WebSocket.CONNECTING:
|
||||||
return <BoardLoading text={"Connessione..."}/>
|
return <BoardLoading text={"Connessione..."}/>
|
||||||
case WebSocket.OPEN:
|
case WebSocket.OPEN:
|
||||||
return <>nothing here</>
|
return <BoardColumns/>
|
||||||
case WebSocket.CLOSING:
|
case WebSocket.CLOSING:
|
||||||
case WebSocket.CLOSED:
|
case WebSocket.CLOSED:
|
||||||
return <BoardError text={"Errore"}/>
|
return <BoardError text={"Errore"}/>
|
||||||
|
|
13
todoblue/src/app/board/[board]/BoardColumns.tsx
Normal file
13
todoblue/src/app/board/[board]/BoardColumns.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import {TaskGroupColumn} from "@/app/board/[board]/TaskGroupColumn"
|
||||||
|
import {useBoardContext} from "@/app/board/[board]/useBoardContext"
|
||||||
|
|
||||||
|
|
||||||
|
export function BoardColumns() {
|
||||||
|
const {taskGroups} = useBoardContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"chapter-5"}>
|
||||||
|
{taskGroups.map((tg) => <TaskGroupColumn taskGroup={tg}/>)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
94
todoblue/src/app/board/[board]/TaskDiv.module.css
Normal file
94
todoblue/src/app/board/[board]/TaskDiv.module.css
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
.taskDiv {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"icon description"
|
||||||
|
;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
align-items: center;
|
||||||
|
min-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskIcon {
|
||||||
|
grid-area: icon;
|
||||||
|
justify-self: start;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskDescription {
|
||||||
|
grid-area: description;
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskDescriptionComplete {
|
||||||
|
text-decoration: 2px currentColor solid line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskPriorityHighest {
|
||||||
|
border: 4px solid hsl(var(--bhsl-current-hue) var(--bhsl-current-saturation) var(--bhsl-current-lightness) / 0.15);
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskPriorityHigh {
|
||||||
|
border: 3px solid hsl(var(--bhsl-current-hue) var(--bhsl-current-saturation) var(--bhsl-current-lightness) / 0.15);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskPriorityNormal {
|
||||||
|
border: 2px solid hsl(var(--bhsl-current-hue) var(--bhsl-current-saturation) var(--bhsl-current-lightness) / 0.15);
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskPriorityLow {
|
||||||
|
border: 1px solid hsl(var(--bhsl-current-hue) var(--bhsl-current-saturation) var(--bhsl-current-lightness) / 0.15);
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskPriorityLowest {
|
||||||
|
border: 0;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskImportanceHighest {
|
||||||
|
--bhsl-current-hue: 100deg;
|
||||||
|
--bhsl-current-saturation: 37%;
|
||||||
|
--bhsl-current-lightness: 68.2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskImportanceHigh {
|
||||||
|
--bhsl-current-hue: 159deg;
|
||||||
|
--bhsl-current-saturation: 30%;
|
||||||
|
--bhsl-current-lightness: 52%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskImportanceNormal {
|
||||||
|
--bhsl-current-hue: 186deg;
|
||||||
|
--bhsl-current-saturation: 47%;
|
||||||
|
--bhsl-current-lightness: 38%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskImportanceLow {
|
||||||
|
--bhsl-current-hue: 203deg;
|
||||||
|
--bhsl-current-saturation: 64%;
|
||||||
|
--bhsl-current-lightness: 32%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskImportanceLowest {
|
||||||
|
--bhsl-current-hue: 237deg;
|
||||||
|
--bhsl-current-saturation: 44%;
|
||||||
|
--bhsl-current-lightness: 31%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes inProgress {
|
||||||
|
0% {
|
||||||
|
filter: brightness(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
filter: brightness(150%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.taskStatusInProgress {
|
||||||
|
animation: 1.7s inProgress ease-out infinite alternate-reverse;
|
||||||
|
}
|
49
todoblue/src/app/board/[board]/TaskDiv.tsx
Normal file
49
todoblue/src/app/board/[board]/TaskDiv.tsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import {TaskIconEl} from "@/app/board/[board]/TaskIconEl"
|
||||||
|
import {Task, TaskWithId} from "@/app/board/[board]/Types"
|
||||||
|
import {useBoardContext} from "@/app/board/[board]/useBoardContext"
|
||||||
|
import {useCallback} from "react"
|
||||||
|
import style from "./TaskDiv.module.css"
|
||||||
|
import cn from "classnames"
|
||||||
|
|
||||||
|
export function TaskDiv({task}: {task: TaskWithId}) {
|
||||||
|
const {send} = useBoardContext()
|
||||||
|
|
||||||
|
const toggleStatus = useCallback(() => {
|
||||||
|
if(task.status === "Unfinished") {
|
||||||
|
send({"Task": [task.id, {...task, status: "Complete"}]})
|
||||||
|
}
|
||||||
|
else if(task.status === "Complete") {
|
||||||
|
send({"Task": [task.id, {...task, status: "Unfinished"}]})
|
||||||
|
}
|
||||||
|
}, [send, task])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div onClick={toggleStatus} tabIndex={0} className={cn({
|
||||||
|
"panel": true,
|
||||||
|
[style.taskDiv]: true,
|
||||||
|
[style.taskPriorityHighest]: task.priority === "Highest",
|
||||||
|
[style.taskPriorityHigh]: task.priority === "High",
|
||||||
|
[style.taskPriorityNormal]: task.priority === "Normal",
|
||||||
|
[style.taskPriorityLow]: task.priority === "Low",
|
||||||
|
[style.taskPriorityLowest]: task.priority === "Lowest",
|
||||||
|
[style.taskImportanceHighest]: task.importance === "Highest",
|
||||||
|
[style.taskImportanceHigh]: task.importance === "High",
|
||||||
|
[style.taskImportanceNormal]: task.importance === "Normal",
|
||||||
|
[style.taskImportanceLow]: task.importance === "Low",
|
||||||
|
[style.taskImportanceLowest]: task.importance === "Lowest",
|
||||||
|
[style.taskStatusUnfinished]: task.status === "Unfinished",
|
||||||
|
[style.taskStatusInProgress]: task.status === "InProgress",
|
||||||
|
[style.taskStatusComplete]: task.status === "Complete",
|
||||||
|
})}>
|
||||||
|
<div className={style.taskIcon}>
|
||||||
|
<TaskIconEl icon={task.icon} style={task.status === "Complete" ? "solid" : "regular"}/>
|
||||||
|
</div>
|
||||||
|
<div className={cn({
|
||||||
|
[style.taskDescription]: true,
|
||||||
|
[style.taskDescriptionComplete]: task.status === "Complete",
|
||||||
|
})}>
|
||||||
|
{task.text}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
.taskGroupColumn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
15
todoblue/src/app/board/[board]/TaskGroupColumn.tsx
Normal file
15
todoblue/src/app/board/[board]/TaskGroupColumn.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import {TaskDiv} from "@/app/board/[board]/TaskDiv"
|
||||||
|
import {TaskGroup} from "@/app/board/[board]/useBoardTaskArranger"
|
||||||
|
import style from "./TaskGroupColumn.module.css"
|
||||||
|
|
||||||
|
|
||||||
|
export function TaskGroupColumn({taskGroup}: {taskGroup: TaskGroup}) {
|
||||||
|
return (
|
||||||
|
<div className={style.taskGroupColumn}>
|
||||||
|
<h3>
|
||||||
|
{taskGroup.name}
|
||||||
|
</h3>
|
||||||
|
{taskGroup.tasks.map(task => <TaskDiv task={task}/>)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
106
todoblue/src/app/board/[board]/TaskIconEl.tsx
Normal file
106
todoblue/src/app/board/[board]/TaskIconEl.tsx
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
import {TaskIcon} from "@/app/board/[board]/Types"
|
||||||
|
import {SizeProp} from "@fortawesome/fontawesome-svg-core"
|
||||||
|
import {
|
||||||
|
faUser as faUserSolid,
|
||||||
|
faImage as faImageSolid,
|
||||||
|
faEnvelope as faEnvelopeSolid,
|
||||||
|
faStar as faStarSolid,
|
||||||
|
faHeart as faHeartSolid,
|
||||||
|
faComment as faCommentSolid,
|
||||||
|
faFaceSmile as faFaceSmileSolid,
|
||||||
|
faFile as faFileSolid,
|
||||||
|
faBell as faBellSolid,
|
||||||
|
faBookmark as faBookmarkSolid,
|
||||||
|
faEye as faEyeSolid,
|
||||||
|
faHand as faHandSolid,
|
||||||
|
faPaperPlane as faPaperPlaneSolid,
|
||||||
|
faHandshake as faHandshakeSolid,
|
||||||
|
faSun as faSunSolid,
|
||||||
|
faClock as faClockSolid,
|
||||||
|
faCircle as faCircleSolid,
|
||||||
|
faSquare as faSquareSolid,
|
||||||
|
faBuilding as faBuildingSolid,
|
||||||
|
faFlag as faFlagSolid,
|
||||||
|
faMoon as faMoonSolid,
|
||||||
|
} from "@fortawesome/free-solid-svg-icons"
|
||||||
|
import {
|
||||||
|
faUser as faUserRegular,
|
||||||
|
faImage as faImageRegular,
|
||||||
|
faEnvelope as faEnvelopeRegular,
|
||||||
|
faStar as faStarRegular,
|
||||||
|
faHeart as faHeartRegular,
|
||||||
|
faComment as faCommentRegular,
|
||||||
|
faFaceSmile as faFaceSmileRegular,
|
||||||
|
faFile as faFileRegular,
|
||||||
|
faBell as faBellRegular,
|
||||||
|
faBookmark as faBookmarkRegular,
|
||||||
|
faEye as faEyeRegular,
|
||||||
|
faHand as faHandRegular,
|
||||||
|
faPaperPlane as faPaperPlaneRegular,
|
||||||
|
faHandshake as faHandshakeRegular,
|
||||||
|
faSun as faSunRegular,
|
||||||
|
faClock as faClockRegular,
|
||||||
|
faCircle as faCircleRegular,
|
||||||
|
faSquare as faSquareRegular,
|
||||||
|
faBuilding as faBuildingRegular,
|
||||||
|
faFlag as faFlagRegular,
|
||||||
|
faMoon as faMoonRegular,
|
||||||
|
} from "@fortawesome/free-regular-svg-icons"
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
|
|
||||||
|
|
||||||
|
export const ICONS = {
|
||||||
|
solid: {
|
||||||
|
"User": faUserSolid,
|
||||||
|
"Image": faImageSolid,
|
||||||
|
"Envelope": faEnvelopeSolid,
|
||||||
|
"Star": faStarSolid,
|
||||||
|
"Heart": faHeartSolid,
|
||||||
|
"Comment": faCommentSolid,
|
||||||
|
"FaceSmile": faFaceSmileSolid,
|
||||||
|
"File": faFileSolid,
|
||||||
|
"Bell": faBellSolid,
|
||||||
|
"Bookmark": faBookmarkSolid,
|
||||||
|
"Eye": faEyeSolid,
|
||||||
|
"Hand": faHandSolid,
|
||||||
|
"PaperPlane": faPaperPlaneSolid,
|
||||||
|
"Handshake": faHandshakeSolid,
|
||||||
|
"Sun": faSunSolid,
|
||||||
|
"Clock": faClockSolid,
|
||||||
|
"Circle": faCircleSolid,
|
||||||
|
"Square": faSquareSolid,
|
||||||
|
"Building": faBuildingSolid,
|
||||||
|
"Flag": faFlagSolid,
|
||||||
|
"Moon": faMoonSolid,
|
||||||
|
},
|
||||||
|
regular: {
|
||||||
|
"User": faUserRegular,
|
||||||
|
"Image": faImageRegular,
|
||||||
|
"Envelope": faEnvelopeRegular,
|
||||||
|
"Star": faStarRegular,
|
||||||
|
"Heart": faHeartRegular,
|
||||||
|
"Comment": faCommentRegular,
|
||||||
|
"FaceSmile": faFaceSmileRegular,
|
||||||
|
"File": faFileRegular,
|
||||||
|
"Bell": faBellRegular,
|
||||||
|
"Bookmark": faBookmarkRegular,
|
||||||
|
"Eye": faEyeRegular,
|
||||||
|
"Hand": faHandRegular,
|
||||||
|
"PaperPlane": faPaperPlaneRegular,
|
||||||
|
"Handshake": faHandshakeRegular,
|
||||||
|
"Sun": faSunRegular,
|
||||||
|
"Clock": faClockRegular,
|
||||||
|
"Circle": faCircleRegular,
|
||||||
|
"Square": faSquareRegular,
|
||||||
|
"Building": faBuildingRegular,
|
||||||
|
"Flag": faFlagRegular,
|
||||||
|
"Moon": faMoonRegular,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function TaskIconEl({icon, style, size}: {icon: TaskIcon, style: "solid" | "regular", size?: SizeProp}) {
|
||||||
|
return (
|
||||||
|
<FontAwesomeIcon icon={ICONS[style][icon]} size={size}/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,17 +1,30 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {TaskWithId} from "@/app/board/[board]/types"
|
import {TaskIconEl} from "@/app/board/[board]/TaskIconEl"
|
||||||
|
import {BoardAction} from "@/app/board/[board]/Types"
|
||||||
|
import {TaskIcon, TaskWithId} from "@/app/board/[board]/types"
|
||||||
import {useBoardWebSocket} from "@/app/board/[board]/useBoardWebSocket"
|
import {useBoardWebSocket} from "@/app/board/[board]/useBoardWebSocket"
|
||||||
import {GroupSortingFunction, TaskGroup, TaskGroupingFunction, TaskSortingFunction, useBoardTaskArranger} from "@/app/board/[board]/useBoardTaskArranger"
|
import {GroupNamingFunction, GroupSortingFunction, TaskGroup, TaskGroupingFunction, TaskSortingFunction, useBoardTaskArranger} from "@/app/board/[board]/useBoardTaskArranger"
|
||||||
import {useBoardTitleEditor} from "@/app/board/[board]/useBoardTitleEditor"
|
import {useBoardTitleEditor} from "@/app/board/[board]/useBoardTitleEditor"
|
||||||
import {useCycleState} from "@/app/useCycleState"
|
import {useCycleState} from "@/app/useCycleState"
|
||||||
|
import {faBell, faBookmark, faBuilding, faCircle, faClock, faComment, faEnvelope, faEye, faFaceSmile, faFile, faFlag, faHand, faHandshake, faHeart, faImage, faMoon, faPaperPlane, faSquare, faStar, faSun, faUser, IconDefinition} from "@fortawesome/free-solid-svg-icons"
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||||
import {Dispatch, SetStateAction} from "react"
|
import {Dispatch, SetStateAction} from "react"
|
||||||
|
|
||||||
function groupTasksByIcon(a: TaskWithId) {return a.icon}
|
function groupTasksByIcon(a: TaskWithId) {return a.icon}
|
||||||
function sortGroupsByKey(a: TaskGroup, b: TaskGroup) {return a.key.localeCompare(b.key)}
|
function sortGroupsByKey(a: TaskGroup, b: TaskGroup) {return a.key.localeCompare(b.key)}
|
||||||
|
|
||||||
const TASK_GROUPERS: [TaskGroupingFunction, GroupSortingFunction][] = [
|
function nameToFontAwesomeIcon(a: string) {
|
||||||
[groupTasksByIcon, sortGroupsByKey],
|
let icon = a as TaskIcon;
|
||||||
|
return <>
|
||||||
|
<TaskIconEl icon={icon} style={"solid"}/>
|
||||||
|
|
||||||
|
{a}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
const TASK_GROUPERS: [TaskGroupingFunction, GroupSortingFunction, GroupNamingFunction][] = [
|
||||||
|
[groupTasksByIcon, sortGroupsByKey, nameToFontAwesomeIcon],
|
||||||
]
|
]
|
||||||
|
|
||||||
function sortTasksByText(a: TaskWithId, b: TaskWithId) {return a.text.localeCompare(b.text)}
|
function sortTasksByText(a: TaskWithId, b: TaskWithId) {return a.text.localeCompare(b.text)}
|
||||||
|
@ -36,15 +49,16 @@ export interface UseBoardReturns {
|
||||||
previousSorter: () => void,
|
previousSorter: () => void,
|
||||||
editTitle: string,
|
editTitle: string,
|
||||||
setEditTitle: Dispatch<SetStateAction<string>>,
|
setEditTitle: Dispatch<SetStateAction<string>>,
|
||||||
|
send: (action: BoardAction) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useBoard(name: string): UseBoardReturns {
|
export function useBoard(name: string): UseBoardReturns {
|
||||||
const {state: {title, tasksById}, send, websocketState} = useBoardWebSocket(name);
|
const {state: {title, tasksById}, send, websocketState} = useBoardWebSocket(name);
|
||||||
|
|
||||||
const {value: [taskGrouper, groupSorter], move: moveGrouper, next: nextGrouper, previous: previousGrouper} = useCycleState(TASK_GROUPERS);
|
const {value: [taskGrouper, groupSorter, groupNamer], move: moveGrouper, next: nextGrouper, previous: previousGrouper} = useCycleState(TASK_GROUPERS);
|
||||||
const {value: taskSorter, move: moveSorter, next: nextSorter, previous: previousSorter} = useCycleState(TASK_SORTERS);
|
const {value: taskSorter, move: moveSorter, next: nextSorter, previous: previousSorter} = useCycleState(TASK_SORTERS);
|
||||||
|
|
||||||
const {taskGroups} = useBoardTaskArranger(tasksById, taskGrouper, groupSorter, taskSorter);
|
const {taskGroups} = useBoardTaskArranger(tasksById, taskGrouper, groupSorter, groupNamer, taskSorter);
|
||||||
const {isEditingTitle, stopEditingTitle, startEditingTitle, toggleEditingTitle, editTitle, setEditTitle} = useBoardTitleEditor(title, send);
|
const {isEditingTitle, stopEditingTitle, startEditingTitle, toggleEditingTitle, editTitle, setEditTitle} = useBoardTitleEditor(title, send);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -63,5 +77,6 @@ export function useBoard(name: string): UseBoardReturns {
|
||||||
previousSorter,
|
previousSorter,
|
||||||
editTitle,
|
editTitle,
|
||||||
setEditTitle,
|
setEditTitle,
|
||||||
|
send,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,18 +1,20 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {Task, TaskWithId} from "@/app/board/[board]/types"
|
import {Task, TaskWithId} from "@/app/board/[board]/types"
|
||||||
import {useMemo} from "react"
|
import {ReactNode, useMemo} from "react"
|
||||||
|
|
||||||
export type TaskGroup = {
|
export type TaskGroup = {
|
||||||
key: string,
|
key: string,
|
||||||
|
name: ReactNode,
|
||||||
tasks: TaskWithId[],
|
tasks: TaskWithId[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TaskGroupingFunction = (a: TaskWithId) => string
|
export type TaskGroupingFunction = (a: TaskWithId) => string
|
||||||
export type TaskSortingFunction = (a: TaskWithId, b: TaskWithId) => number;
|
|
||||||
export type GroupSortingFunction = (a: TaskGroup, b: TaskGroup) => number;
|
export type GroupSortingFunction = (a: TaskGroup, b: TaskGroup) => number;
|
||||||
|
export type GroupNamingFunction = (a: string) => ReactNode;
|
||||||
|
export type TaskSortingFunction = (a: TaskWithId, b: TaskWithId) => number;
|
||||||
|
|
||||||
export function arrangeBoardTasks(tasksById: { [p: string]: Task }, taskGrouper: TaskGroupingFunction, groupSorter: GroupSortingFunction, taskSorter: TaskSortingFunction): TaskGroup[] {
|
export function arrangeBoardTasks(tasksById: { [p: string]: Task }, taskGrouper: TaskGroupingFunction, groupSorter: GroupSortingFunction, groupNamer: GroupNamingFunction, taskSorter: TaskSortingFunction): TaskGroup[] {
|
||||||
const groupsByKey: {[group: string]: TaskWithId[]} = {}
|
const groupsByKey: {[group: string]: TaskWithId[]} = {}
|
||||||
|
|
||||||
for(const [id, task] of Object.entries(tasksById)) {
|
for(const [id, task] of Object.entries(tasksById)) {
|
||||||
|
@ -31,7 +33,8 @@ export function arrangeBoardTasks(tasksById: { [p: string]: Task }, taskGrouper:
|
||||||
const groups: TaskGroup[] = []
|
const groups: TaskGroup[] = []
|
||||||
|
|
||||||
for(const [key, tasks] of Object.entries(groupsByKey)) {
|
for(const [key, tasks] of Object.entries(groupsByKey)) {
|
||||||
groups.push({key, tasks})
|
const name = groupNamer(key);
|
||||||
|
groups.push({key, name, tasks})
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.sort(groupSorter)
|
groups.sort(groupSorter)
|
||||||
|
@ -40,8 +43,8 @@ export function arrangeBoardTasks(tasksById: { [p: string]: Task }, taskGrouper:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function useBoardTaskArranger(tasksById: { [p: string]: Task }, taskGrouper: TaskGroupingFunction, groupSorter: GroupSortingFunction, taskSorter: TaskSortingFunction) {
|
export function useBoardTaskArranger(tasksById: { [p: string]: Task }, taskGrouper: TaskGroupingFunction, groupSorter: GroupSortingFunction, groupNamer: GroupNamingFunction, taskSorter: TaskSortingFunction) {
|
||||||
const taskGroups = useMemo(() => arrangeBoardTasks(tasksById, taskGrouper, groupSorter, taskSorter), [tasksById, taskGrouper, taskSorter, groupSorter])
|
const taskGroups = useMemo(() => arrangeBoardTasks(tasksById, taskGrouper, groupSorter, groupNamer, taskSorter), [tasksById, taskGrouper, taskSorter, groupSorter])
|
||||||
|
|
||||||
return {taskGroups};
|
return {taskGroups};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@ import {useMemo} from "react"
|
||||||
|
|
||||||
|
|
||||||
export function useBoardWebSocketURL(name: string) {
|
export function useBoardWebSocketURL(name: string) {
|
||||||
const webSocketURL = useMemo(() => `ws://127.0.0.1:8080/board/${name}/ws`, [name]);
|
const webSocketURL = useMemo(() => `${process.env.NEXT_PUBLIC_API_BASE_URL}/board/${name}/ws`, [name]);
|
||||||
return {webSocketURL}
|
return {webSocketURL}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ export default function RootLayout({children}: { children: ReactNode }) {
|
||||||
<p>
|
<p>
|
||||||
© <a href="https://steffo.eu">Stefano Pigozzi</a> -
|
© <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://www.gnu.org/licenses/agpl-3.0.en.html">AGPL 3.0</a> -
|
||||||
<a href="https://github.com/Steffo99/todocolors">GitHub</a>
|
<a href="https://github.com/Steffo99/todocolors">GitHub</a> -
|
||||||
|
Using {process.env.NEXT_PUBLIC_API_BASE_URL}
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in a new issue