Feature pack: Media, Gamification, Templates, Time Tracking, Kanban, AI Briefing, Webhooks, Icon UI

API features (separate files in /api/src/features/):
- media-input: upload text/audio/photo/video, transcription
- gamification: points, streaks, badges, leaderboard
- templates: predefined task sets (sprint, study, moving)
- time-tracking: start/stop timer, task/user reports
- kanban: board view, drag-and-drop move
- ai-briefing: daily AI summary with tasks/goals/reviews
- webhooks-outgoing: notify external systems on events

UI components (separate files in /components/features/):
- IconButton: icon-only buttons with tooltip
- CompactHeader, PageActionBar, InlineEditField
- TaskDetailActions, GoalActionButtons, CollabActionButtons
- DeleteIconButton, CollabBackButton

All features modular — registry.js enables/disables each one.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 01:13:09 +00:00
parent f9c4ec631c
commit 8cf14dcf59
14 changed files with 875 additions and 200 deletions

View File

@@ -0,0 +1,63 @@
'use client';
import IconButton from './IconButton';
interface Props {
onPlan: () => void;
onReport: () => void;
onDelete: () => void;
planLoading: boolean;
reportLoading: boolean;
t: (key: string) => string;
}
export default function GoalActionButtons({ onPlan, onReport, onDelete, planLoading, reportLoading, t }: Props) {
return (
<div className="flex items-center gap-2">
<IconButton
icon={
planLoading ? (
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-current" />
) : (
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
)
}
label={t("goals.plan")}
onClick={onPlan}
disabled={planLoading}
variant="purple"
size="lg"
/>
<IconButton
icon={
reportLoading ? (
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-current" />
) : (
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
)
}
label={t("goals.report")}
onClick={onReport}
disabled={reportLoading}
variant="success"
size="lg"
/>
<IconButton
icon={
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
}
label={t("tasks.delete")}
onClick={onDelete}
variant="danger"
size="lg"
/>
</div>
);
}