feat(user-avatars): Avatar-Component (Initialen-Badge) + überall einsetzen [tsc:fail]
This commit is contained in:
parent
68158b7cb7
commit
985f914260
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"completed_features": [],
|
"completed_features": [],
|
||||||
"current_feature": "time-rounding-rules",
|
"current_feature": "user-avatars",
|
||||||
"started_at": "2026-05-23T06:33:48.406343",
|
"started_at": "2026-05-23T06:33:48.406343",
|
||||||
"attempted_features": [
|
"attempted_features": [
|
||||||
"invoicing-stub"
|
"invoicing-stub",
|
||||||
|
"time-rounding-rules"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1478,3 +1478,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
|
||||||
|
- `06:36:29` **INFO** Committed feature time-rounding-rules
|
||||||
|
- `06:36:29` **INFO** Pushed: rc=0
|
||||||
|
|
||||||
|
## Phase-3 Feature: user-avatars (2026-05-23 06:36:29)
|
||||||
|
|
||||||
|
- `06:36:29` **INFO** Description: Avatar-Component (Initialen-Badge) + überall einsetzen
|
||||||
|
- `06:36:29` **INFO** Generating apps/web/src/components/Avatar.tsx (Avatar-Component. Props: name (string), size? ('sm'|'md'|'lg', default…)
|
||||||
|
- `06:36:41` **INFO** wrote 1067 chars in 12.0s (attempt 1)
|
||||||
|
- `06:36:41` **INFO** Generating apps/web/src/components/Nav.tsx (ERWEITERT — füge <Avatar name={user.name} size='sm' /> links neben Log…)
|
||||||
|
- `06:37:38` **INFO** wrote 6000 chars in 56.9s (attempt 1)
|
||||||
|
- `06:37:38` **INFO** Running tsc --noEmit on api…
|
||||||
|
- `06:37:40` **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
|
||||||
|
|||||||
45
apps/web/src/components/Avatar.tsx
Normal file
45
apps/web/src/components/Avatar.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface AvatarProps {
|
||||||
|
name: string;
|
||||||
|
size?: 'sm' | 'md' | 'lg';
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLORS = [
|
||||||
|
'bg-orange-500',
|
||||||
|
'bg-blue-500',
|
||||||
|
'bg-emerald-500',
|
||||||
|
'bg-purple-500',
|
||||||
|
'bg-pink-500',
|
||||||
|
];
|
||||||
|
|
||||||
|
const SIZE_MAP = {
|
||||||
|
sm: 'w-8 h-8 text-xs',
|
||||||
|
md: 'w-10 h-10 text-sm',
|
||||||
|
lg: 'w-12 h-12 text-base',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Avatar: React.FC<AvatarProps> = ({ name, size = 'md' }) => {
|
||||||
|
const getInitials = (name: string) => {
|
||||||
|
const parts = name.trim().split(/\s+/);
|
||||||
|
if (parts.length === 1) return parts[0].substring(0, 2).toUpperCase();
|
||||||
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getColor = (name: string) => {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < name.length; i++) {
|
||||||
|
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
const index = Math.abs(hash) % COLORS.length;
|
||||||
|
return COLORS[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex items-center justify-center rounded-full text-white font-medium ${SIZE_MAP[size]} ${getColor(name)}`}
|
||||||
|
>
|
||||||
|
{getInitials(name)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -15,11 +15,13 @@ import {
|
|||||||
X,
|
X,
|
||||||
Zap,
|
Zap,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
Languages
|
Languages,
|
||||||
|
LogOut
|
||||||
} 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"
|
||||||
import { useTheme } from "../lib/theme"
|
import { useTheme } from "../lib/theme"
|
||||||
|
import Avatar from "./Avatar"
|
||||||
|
|
||||||
export default function Nav() {
|
export default function Nav() {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
@ -96,7 +98,7 @@ export default function Nav() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
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"
|
||||||
@ -108,18 +110,31 @@ export default function Nav() {
|
|||||||
<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="flex items-center gap-1 p-1 rounded-full bg-gray-100 dark:bg-slate-800 border border-gray-200 dark:border-slate-700">
|
||||||
<button
|
<button
|
||||||
onClick={() => setLang('en')}
|
onClick={() => setLang('en')}
|
||||||
className={`px-2 py-1 text-[10px] font-bold rounded-full transition-all ${lang === 'en' ? 'bg-white dark:bg-slate-600 text-indigo-600 dark:text-white shadow-sm' : 'text-gray-500 dark:text-slate-400 hover:text-gray-700 dark:hover:text-slate-200'}`}
|
className={`px-2 py-1 text-[10px] font-bold rounded-full transition-colors ${lang === 'en' ? 'bg-white dark:bg-slate-700 text-indigo-600 dark:text-indigo-400 shadow-sm' : 'text-gray-500 dark:text-slate-400 hover:text-gray-700 dark:hover:text-slate-300'}`}
|
||||||
>
|
>
|
||||||
EN
|
EN
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setLang('de')}
|
onClick={() => setLang('de')}
|
||||||
className={`px-2 py-1 text-[10px] font-bold rounded-full transition-all ${lang === 'de' ? 'bg-white dark:bg-slate-600 text-indigo-600 dark:text-white shadow-sm' : 'text-gray-500 dark:text-slate-400 hover:text-gray-700 dark:hover:text-slate-200'}`}
|
className={`px-2 py-1 text-[10px] font-bold rounded-full transition-colors ${lang === 'de' ? 'bg-white dark:bg-slate-700 text-indigo-600 dark:text-indigo-400 shadow-sm' : 'text-gray-500 dark:text-slate-400 hover:text-gray-700 dark:hover:text-slate-300'}`}
|
||||||
>
|
>
|
||||||
DE
|
DE
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3 pl-3 border-l border-gray-200 dark:border-slate-800">
|
||||||
|
{user && (
|
||||||
|
<Avatar name={user.name} size="sm" />
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => api.logout()}
|
||||||
|
className="p-2 text-gray-500 hover:text-red-600 dark:text-slate-400 dark:hover:text-red-400 transition-colors"
|
||||||
|
title="Logout"
|
||||||
|
>
|
||||||
|
<LogOut className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
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"
|
className="md:hidden p-2 rounded-md text-gray-500 hover:bg-gray-100 dark:text-slate-400 dark:hover:bg-slate-800"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user