feat(ui): Jira-style card detail, edit-mode column management, design polish
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useReducer, useRef } from "preact/hooks";
|
||||
import { useCallback, useEffect, useReducer, useRef, useState } from "preact/hooks";
|
||||
import { api, APIError } from "./api";
|
||||
import {
|
||||
addCard,
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { movePosition } from "./drag";
|
||||
import type { CardPatch, ListPatch } from "./types";
|
||||
import { BoardCanvas } from "./components/BoardCanvas";
|
||||
import { CardDetailModal } from "./components/CardDetailModal";
|
||||
import { Header } from "./components/Header";
|
||||
import { Toasts } from "./components/Toast";
|
||||
|
||||
@@ -27,9 +28,18 @@ function friendlyError(error: unknown): string {
|
||||
|
||||
export function App() {
|
||||
const [state, dispatch] = useReducer(boardReducer, initialBoardState);
|
||||
const [managingLists, setManagingLists] = useState(false);
|
||||
const [selectedCardID, setSelectedCardID] = useState<number | null>(null);
|
||||
const boardRef = useRef(state.board);
|
||||
boardRef.current = state.board;
|
||||
|
||||
const selectedList = state.board?.lists.find((list) => list.cards.some((card) => card.id === selectedCardID));
|
||||
const selectedCard = selectedList?.cards.find((card) => card.id === selectedCardID);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedCardID !== null && !selectedCard) setSelectedCardID(null);
|
||||
}, [selectedCardID, selectedCard]);
|
||||
|
||||
const toast = useCallback((message: string, tone: "success" | "error" | "info" = "info") => {
|
||||
const id = ++toastID;
|
||||
dispatch({ type: "toast", toast: { id, message, tone } });
|
||||
@@ -141,7 +151,12 @@ export function App() {
|
||||
|
||||
return (
|
||||
<main class="app-shell">
|
||||
<Header onReload={load} loading={state.loading} />
|
||||
<Header
|
||||
onReload={load}
|
||||
loading={state.loading}
|
||||
managingLists={managingLists}
|
||||
onToggleManagingLists={() => setManagingLists((active) => !active)}
|
||||
/>
|
||||
{state.loading && !state.board ? <section class="loading-card" aria-live="polite">Loading your Haven board…</section> : null}
|
||||
{state.error ? (
|
||||
<section class="fatal-state">
|
||||
@@ -158,12 +173,21 @@ export function App() {
|
||||
onDeleteList={deleteList}
|
||||
onCreateCard={createCard}
|
||||
onEditCard={editCard}
|
||||
onDeleteCard={deleteCard}
|
||||
onMoveCard={moveCard}
|
||||
onOpenCard={setSelectedCardID}
|
||||
managingLists={managingLists}
|
||||
/>
|
||||
) : null}
|
||||
{selectedCard && selectedList ? (
|
||||
<CardDetailModal
|
||||
card={selectedCard}
|
||||
listName={selectedList.name}
|
||||
onClose={() => setSelectedCardID(null)}
|
||||
onEdit={(patch) => editCard(selectedCard.id, patch)}
|
||||
onDelete={() => deleteCard(selectedCard.id)}
|
||||
/>
|
||||
) : null}
|
||||
<Toasts toasts={state.toasts} onDismiss={(id) => dispatch({ type: "dismiss", id })} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user