- Add Projects page with Sprint Board and Backlog views - Copy SprintBoard and BacklogView components to components/gantt/ - Copy useTaskStore for project/task/sprint management - Add API routes for task persistence with SQLite - Add UI components: dialog, select, table, textarea - Add avatar and attachment utilities - Update sidebar with Projects navigation link - Remove static export config to support API routes - Add dist to .gitignore
59 lines
1.8 KiB
TypeScript
59 lines
1.8 KiB
TypeScript
const AVATAR_PALETTES = [
|
|
["#1f2937", "#3b82f6"],
|
|
["#0f172a", "#22c55e"],
|
|
["#172554", "#06b6d4"],
|
|
["#3f1d2e", "#ec4899"],
|
|
["#1f2937", "#f59e0b"],
|
|
["#111827", "#a855f7"],
|
|
["#052e16", "#14b8a6"],
|
|
["#3f3f46", "#e11d48"],
|
|
] as const;
|
|
|
|
export function getInitials(name?: string): string {
|
|
return (name || "User")
|
|
.split(/\s+/)
|
|
.filter(Boolean)
|
|
.slice(0, 2)
|
|
.map((part) => part[0]?.toUpperCase() || "")
|
|
.join("") || "U";
|
|
}
|
|
|
|
function hashSeed(seed: string): number {
|
|
let hash = 0;
|
|
for (let i = 0; i < seed.length; i += 1) {
|
|
hash = (hash * 31 + seed.charCodeAt(i)) >>> 0;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
export function generateAvatarDataUrl(seed: string, name?: string, paletteOffset = 0): string {
|
|
const safeSeed = seed || "default";
|
|
const hash = hashSeed(safeSeed);
|
|
const paletteIndex = (hash + paletteOffset) % AVATAR_PALETTES.length;
|
|
const palette = AVATAR_PALETTES[paletteIndex];
|
|
const initials = getInitials(name);
|
|
|
|
const svg = `
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
|
|
<defs>
|
|
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
|
<stop offset="0%" stop-color="${palette[0]}"/>
|
|
<stop offset="100%" stop-color="${palette[1]}"/>
|
|
</linearGradient>
|
|
</defs>
|
|
<rect width="128" height="128" fill="url(#g)"/>
|
|
<circle cx="64" cy="64" r="52" fill="rgba(255,255,255,0.12)"/>
|
|
<text x="64" y="78" text-anchor="middle" font-family="Arial, sans-serif" font-size="44" font-weight="700" fill="white">${initials}</text>
|
|
</svg>`;
|
|
|
|
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
|
}
|
|
|
|
export function buildAvatarPresets(seed: string, name?: string, count = 6): string[] {
|
|
const urls: string[] = [];
|
|
for (let i = 0; i < count; i += 1) {
|
|
urls.push(generateAvatarDataUrl(`${seed}:${i}`, name, i));
|
|
}
|
|
return urls;
|
|
}
|