diff --git a/manifest.json b/manifest.json index f40ad06..bcffeb7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "JiraTime", - "version": "1.4.0", + "version": "1.5.0", "description": "Simple Jira Time Tracking for Developers. By yours truly Bernhard Dorn.", "author": "Bernhard Dorn", "action": { diff --git a/package.json b/package.json index 822072f..b70c131 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jiratime", "private": true, - "version": "1.4.0", + "version": "1.5.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/TicketItem.tsx b/src/components/TicketItem.tsx index 4a6dc1a..78f89ae 100644 --- a/src/components/TicketItem.tsx +++ b/src/components/TicketItem.tsx @@ -1,14 +1,14 @@ import { useState, useEffect } from "react"; import type { JiraTicket, AppSettings } from "../lib/types"; import type { ActiveTimer } from "../hooks/useActiveTimer"; -import { addWorklog } from "../lib/jira"; +import { addWorklog, checkWorklogPermission } from "../lib/jira"; import { formatDuration, formatDurationFromStart, parseDuration, cn } from "../lib/utils"; import { Button } from "./ui/Button"; import { Input } from "./ui/Input"; import { Play, Square, ExternalLink, ChevronDown, ChevronUp, Clock, Bug, CheckSquare, Bookmark, Zap, GitCommit, FileQuestion, - HelpCircle, Microscope, PinOff + HelpCircle, Microscope, PinOff, Trash2, RotateCcw } from "lucide-react"; // Helper for Issue Type Icon @@ -84,9 +84,11 @@ export const TicketItem = ({ const [description, setDescription] = useState(() => { return localStorage.getItem(getStorageKey()) || ""; }); + const [lastErrorMessage, setLastErrorMessage] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [liveDuration, setLiveDuration] = useState(""); + const [hasError, setHasError] = useState(false); const isTimerRunning = activeTimer?.ticketId === ticket.id; @@ -129,6 +131,8 @@ export const TicketItem = ({ if (!manualTime) return; setIsSubmitting(true); + setHasError(false); // Clear previous errors + setLastErrorMessage(""); try { // Check Easter Egg const seconds = parseDuration(manualTime); @@ -147,7 +151,10 @@ export const TicketItem = ({ // Optional: Show success feedback } catch (error) { console.error(error); - alert("Failed to log time. Check console."); + setHasError(true); + const msg = error instanceof Error ? error.message : "Failed to log time."; + setLastErrorMessage(msg); + // alert(msg); // Removed alert to rely on in-component display } finally { setIsSubmitting(false); } @@ -157,6 +164,8 @@ export const TicketItem = ({ if (!activeTimer) return; setIsSubmitting(true); + setHasError(false); // Clear previous errors + setLastErrorMessage(""); try { let seconds = Math.floor((Date.now() - activeTimer.startTime) / 1000); @@ -179,7 +188,52 @@ export const TicketItem = ({ onRefresh(); } catch (error) { console.error(error); - alert("Failed to save timer. Check console."); + setHasError(true); + const msg = error instanceof Error ? error.message : "Failed to save timer."; + setLastErrorMessage(msg); + // alert(msg); // Removed alert to rely on in-component display + } finally { + setIsSubmitting(false); + } + }; + + const handleDiscardTimer = () => { + if (!confirm("Are you sure you want to discard the currently tracked time? This cannot be undone.")) return; + + onStopTimer(); + setDescription(""); + setHasError(false); // Clear errors on discard + setLastErrorMessage(""); + localStorage.removeItem(getStorageKey()); + onRefresh(); + }; + + const handleStartTimerWithCheck = async () => { + setIsSubmitting(true); + setHasError(false); // Clear previous errors + setLastErrorMessage(""); + try { + const hasPermission = await checkWorklogPermission(settings, ticket.key); + if (!hasPermission) { + const msg = `You do not have permission to log work on ${ticket.key}.`; + setHasError(true); + setLastErrorMessage(msg); + // alert(msg); // Removed alert to rely on in-component display + return; + } + onStartTimer(ticket.id); + } catch (error) { + console.error("Failed to check permissions:", error); + const msg = error instanceof Error ? error.message : "Failed to check permissions."; + setHasError(true); + setLastErrorMessage(msg); + // Fallback: allow starting if check fails? Or block? + // Better to block if we can't be sure, or just warn. + if (confirm(`Could not verify permissions: ${msg}. Start timer anyway?`)) { + setHasError(false); // Clear error if user proceeds + setLastErrorMessage(""); + onStartTimer(ticket.id); + } } finally { setIsSubmitting(false); } @@ -265,21 +319,33 @@ export const TicketItem = ({ {/* Timer Section */}