feat(router-phase21): Mount /templates Route + Nav-Link + UndoStack global [tsc:fail]

This commit is contained in:
Dennis (via Claude+Gemma) 2026-05-23 08:16:43 +02:00
parent e5877d05d6
commit 8248eb910f
4 changed files with 70 additions and 35 deletions

View File

@ -1,11 +1,12 @@
{ {
"completed_features": [], "completed_features": [],
"current_feature": "time-entry-templates-page", "current_feature": "router-phase21",
"started_at": "2026-05-23T08:09:40.135892", "started_at": "2026-05-23T08:09:40.135892",
"attempted_features": [ "attempted_features": [
"keyboard-undo-stack", "keyboard-undo-stack",
"snippet-shortcuts", "snippet-shortcuts",
"smart-rounding-on-input", "smart-rounding-on-input",
"color-coded-customer-rows" "color-coded-customer-rows",
"time-entry-templates-page"
] ]
} }

View File

@ -2567,3 +2567,22 @@ src/index.ts(27,25): error TS2769: No overload matches this call.
Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTy Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTy
- `08:14:55` **INFO** Committed feature time-entry-templates-page
- `08:14:55` **INFO** Pushed: rc=0
## Phase-3 Feature: router-phase21 (2026-05-23 08:14:55)
- `08:14:55` **INFO** Description: Mount /templates Route + Nav-Link + UndoStack global
- `08:14:55` **INFO** Generating apps/web/src/App.tsx (ERWEITERT — füge /time-entry-templates Route + mount <UndoStack /> glo…)
- `08:15:54` **INFO** wrote 7100 chars in 58.2s (attempt 1)
- `08:15:54` **INFO** Generating apps/web/src/components/Nav.tsx (ERWEITERT — füge Templates-Link in Nav. Behalte alles.…)
- `08:16:42` **INFO** wrote 5110 chars in 48.1s (attempt 1)
- `08:16:42` **INFO** Running tsc --noEmit on api…
- `08:16:43` **WARN** tsc errors:
src/index.ts(27,25): error TS2769: No overload matches this call.
Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }, done: (err?: Error | undefined) => void): void'.
Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTy

View File

@ -19,6 +19,7 @@ import TwoFactorAuth from "./pages/TwoFactorAuth"
import Billing from "./pages/Billing" import Billing from "./pages/Billing"
import Integrations from "./pages/Integrations" import Integrations from "./pages/Integrations"
import ProjectTemplates from "./pages/ProjectTemplates" import ProjectTemplates from "./pages/ProjectTemplates"
import TimeEntryTemplates from "./pages/TimeEntryTemplates"
import Invoices from "./pages/Invoices" import Invoices from "./pages/Invoices"
import ApiKeys from "./pages/ApiKeys" import ApiKeys from "./pages/ApiKeys"
import AcceptInvite from "./pages/AcceptInvite" import AcceptInvite from "./pages/AcceptInvite"
@ -29,6 +30,7 @@ import OnboardingTour from "./components/OnboardingTour"
import VersionBadge from "./components/VersionBadge" import VersionBadge from "./components/VersionBadge"
import QuickAdd from "./components/QuickAdd" import QuickAdd from "./components/QuickAdd"
import IdleDetector from "./components/IdleDetector" import IdleDetector from "./components/IdleDetector"
import UndoStack from "./components/UndoStack"
import { ToastProvider } from "./components/Toast" import { ToastProvider } from "./components/Toast"
import ErrorBoundary from "./components/ErrorBoundary" import ErrorBoundary from "./components/ErrorBoundary"
import { api } from "./lib/api" import { api } from "./lib/api"
@ -44,6 +46,7 @@ const rootRoute = createRootRoute({
<KeyboardHelp /> <KeyboardHelp />
<OnboardingTour /> <OnboardingTour />
<QuickAdd /> <QuickAdd />
<UndoStack />
<Outlet /> <Outlet />
</div> </div>
<footer className="border-t bg-white py-2 text-center"> <footer className="border-t bg-white py-2 text-center">
@ -118,6 +121,13 @@ const timeEntriesRoute = createRoute({
component: TimeEntries component: TimeEntries
}) })
const timeEntryTemplatesRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/time-entry-templates",
beforeLoad: authCheck,
component: TimeEntryTemplates
})
const calendarRoute = createRoute({ const calendarRoute = createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: "/calendar", path: "/calendar",
@ -153,6 +163,13 @@ const projectDetailRoute = createRoute({
component: ProjectDetail component: ProjectDetail
}) })
const projectTemplatesRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/project-templates",
beforeLoad: authCheck,
component: ProjectTemplates
})
const profileRoute = createRoute({ const profileRoute = createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: "/profile", path: "/profile",
@ -216,16 +233,9 @@ const integrationsRoute = createRoute({
component: Integrations component: Integrations
}) })
const projectTemplatesRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/admin/project-templates",
beforeLoad: adminCheck,
component: ProjectTemplates
})
const invoicesRoute = createRoute({ const invoicesRoute = createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
path: "/billing/invoices", path: "/invoices",
beforeLoad: authCheck, beforeLoad: authCheck,
component: Invoices component: Invoices
}) })
@ -237,18 +247,20 @@ const apiKeysRoute = createRoute({
component: ApiKeys component: ApiKeys
}) })
const routeTree = rootRoute.addChildren([ const routeTree = [
indexRoute, indexRoute,
loginRoute, loginRoute,
forgotPasswordRoute, forgotPasswordRoute,
resetPasswordRoute, resetPasswordRoute,
acceptInviteRoute, acceptInviteRoute,
timeEntriesRoute, timeEntriesRoute,
timeEntryTemplatesRoute,
calendarRoute, calendarRoute,
customersRoute, customersRoute,
customerDetailRoute, customerDetailRoute,
projectsRoute, projectsRoute,
projectDetailRoute, projectDetailRoute,
projectTemplatesRoute,
profileRoute, profileRoute,
adminUsersRoute, adminUsersRoute,
settingsRoute, settingsRoute,
@ -258,12 +270,14 @@ const routeTree = rootRoute.addChildren([
twoFactorRoute, twoFactorRoute,
billingRoute, billingRoute,
integrationsRoute, integrationsRoute,
projectTemplatesRoute,
invoicesRoute, invoicesRoute,
apiKeysRoute apiKeysRoute
]) ]
const router = createRouter({ routeTree }) const router = createRouter({
routeTree,
defaultPreload: 'intent'
})
declare module "@tanstack/react-router" { declare module "@tanstack/react-router" {
interface Register { interface Register {

View File

@ -17,7 +17,8 @@ import {
CreditCard, CreditCard,
Languages, Languages,
LogOut, LogOut,
FileText FileText,
LayoutTemplate
} from "lucide-react" } from "lucide-react"
import { useQuery } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
import { api } from "../lib/api" import { api } from "../lib/api"
@ -42,6 +43,7 @@ export default function Nav() {
{ label: "Customers", to: "/customers", icon: Users }, { label: "Customers", to: "/customers", icon: Users },
{ label: "Projects", to: "/projects", icon: FolderKanban }, { label: "Projects", to: "/projects", icon: FolderKanban },
{ label: "Invoices", to: "/invoices", icon: FileText }, { label: "Invoices", to: "/invoices", icon: FileText },
{ label: "Templates", to: "/templates", icon: LayoutTemplate },
{ label: "Integrations", to: "/integrations", icon: Zap }, { label: "Integrations", to: "/integrations", icon: Zap },
{ label: "Billing", to: "/billing", icon: CreditCard }, { label: "Billing", to: "/billing", icon: CreditCard },
] ]
@ -114,33 +116,32 @@ export default function Nav() {
onClick={toggleTheme} onClick={toggleTheme}
aria-label={theme === 'dark' ? "Switch to light mode" : "Switch to dark mode"} aria-label={theme === 'dark' ? "Switch to light mode" : "Switch to dark mode"}
className="p-2 rounded-full text-gray-500 hover:bg-gray-100 dark:text-slate-400 dark:hover:bg-slate-800 transition-colors" className="p-2 rounded-full text-gray-500 hover:bg-gray-100 dark:text-slate-400 dark:hover:bg-slate-800 transition-colors"
title="Toggle Theme"
> >
{theme === 'dark' ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />} {theme === 'dark' ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
</button> </button>
<div className="flex items-center gap-1 p-1 rounded-full bg-gray-100 dark:bg-slate-800 border border-gray-200 dark:border-slate-700"> <div className="h-6 w-px bg-gray-200 dark:bg-slate-800 mx-1" />
<button
onClick={() => setLang(lang === 'en' ? 'de' : 'en')}
aria-label={`Switch language to ${lang === 'en' ? 'German' : 'English'}`}
className="p-1.5 rounded-full text-gray-600 hover:bg-white dark:text-slate-400 dark:hover:bg-slate-700 transition-colors"
>
<Languages className="w-4 h-4" />
</button>
<div className="w-px h-4 bg-gray-300 dark:bg-slate-600 mx-1" />
<button
onClick={() => api.logout()}
aria-label="Logout"
className="p-1.5 rounded-full text-gray-600 hover:bg-white dark:text-slate-400 dark:hover:bg-slate-700 transition-colors"
>
<LogOut className="w-4 h-4" />
</button>
</div>
<Avatar user={user} /> <Avatar user={user} />
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="md:hidden p-2 rounded-md text-gray-500 hover:bg-gray-100 dark:text-slate-400 dark:hover:bg-slate-800"
>
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
</div> </div>
</div> </div>
</div> </div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<div className="md:hidden border-t border-gray-200 dark:border-slate-800 bg-white dark:bg-slate-900 px-4 py-4 space-y-1">
{allItems.map((item) => (
<NavLink key={item.to} item={item} />
))}
</div>
)}
</nav> </nav>
) )
} }