Unterstützung: Mitarbeiter tragen sich selbst als Unterstützung ein
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d83bbfb48d
commit
bc9d673ff7
4 files changed
+141
-4
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import { useState } from 'react'
|
||||
import { FaTimes } from 'react-icons/fa'
|
||||
import { parseSupporters, supportMinutes } from '../lib/support'
|
||||
import { stepLabel } from '../lib/timeSteps'
|
||||
|
||||
/* "Ich habe unterstuetzt": die eigene Unterstuetzung bei einem fremden Arbeitsblatt
|
||||
eintragen, aendern oder entfernen. Andere Eintraege und die Zeiten bleiben unberuehrt. */
|
||||
export default function OwnSupportModal({ worksheet, currentUserId, onClose, onSave }) {
|
||||
const existing = parseSupporters(worksheet.supporters).find((s) => s.employeeId === currentUserId)
|
||||
const [task, setTask] = useState(existing?.task || '')
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
|
||||
const save = async (value) => {
|
||||
setSaving(true)
|
||||
setError('')
|
||||
const result = await onSave(worksheet, value)
|
||||
if (result?.success) {
|
||||
onClose()
|
||||
return
|
||||
}
|
||||
setError(result?.error || 'Speichern fehlgeschlagen')
|
||||
setSaving(false)
|
||||
}
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
if (!task.trim()) {
|
||||
setError('Bitte angeben, wobei du unterstützt hast.')
|
||||
return
|
||||
}
|
||||
save(task.trim())
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overlay">
|
||||
<span className="overlay-close" onClick={onClose}>
|
||||
<FaTimes />
|
||||
</span>
|
||||
<div className="overlay-content" style={{ maxWidth: 480 }}>
|
||||
<h2 className="mb-2">Unterstützung eintragen</h2>
|
||||
<p className="text-grey" style={{ fontSize: '14px', marginBottom: '16px' }}>
|
||||
WSID {worksheet.wsid} · {worksheet.employeeName}
|
||||
<br />Dir werden {stepLabel(supportMinutes(worksheet.totalTime))} angerechnet.
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red text-white p-2 mb-2" style={{ borderRadius: '4px' }}>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="own-support-task">Wobei?</label>
|
||||
<input
|
||||
id="own-support-task"
|
||||
className="form-control"
|
||||
maxLength={200}
|
||||
required
|
||||
value={task}
|
||||
onChange={(e) => setTask(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center mt-2 flex gap-2" style={{ justifyContent: 'center' }}>
|
||||
<button type="submit" className="btn btn-dark" disabled={saving}>
|
||||
{saving ? 'Speichert...' : 'Speichern'}
|
||||
</button>
|
||||
{existing && (
|
||||
<button type="button" className="btn" disabled={saving} onClick={() => save(null)}>
|
||||
Eintrag entfernen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import EditorDropdown from './EditorDropdown'
|
||||
import CreateWorksheetModal from './CreateWorksheetModal'
|
||||
import WorksheetList from './WorksheetList'
|
||||
import WorksheetTimesModal from './WorksheetTimesModal'
|
||||
import OwnSupportModal from './OwnSupportModal'
|
||||
import WorksheetSummary from './WorksheetSummary'
|
||||
import TicketProjects from './TicketProjects'
|
||||
import ProjectContextPanel from './ProjectContextPanel'
|
||||
@@ -20,6 +21,7 @@ import { useCustomerAbo } from '../hooks/useCustomerAbo'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
import { databases, DATABASE_ID, COLLECTIONS } from '../lib/appwrite'
|
||||
import { canEditWorksheetTimes } from '../lib/ticketForm'
|
||||
import { canAddOwnSupport } from '../lib/support'
|
||||
import { ticketTypeLabel } from '../lib/ticketStatus'
|
||||
|
||||
// Projekt / Preview steht mit im Details-Reiter
|
||||
@@ -35,13 +37,14 @@ export default function TicketCard({ ticket, onUpdate }) {
|
||||
const [activeTab, setActiveTab] = useState('details')
|
||||
const [showCreateWorksheet, setShowCreateWorksheet] = useState(false)
|
||||
const [editTimesWorksheet, setEditTimesWorksheet] = useState(null)
|
||||
const [ownSupportWorksheet, setOwnSupportWorksheet] = useState(null)
|
||||
// Abo-Stundenkonto gibt es nur bei Wartungs-Abo; erst laden, wenn Arbeitsblaetter gebraucht werden
|
||||
const { hoursAllowed: aboHoursAllowed } = useCustomerAbo(
|
||||
ticket.customerId,
|
||||
(expanded && activeTab === 'worksheets') || showCreateWorksheet
|
||||
)
|
||||
|
||||
const { worksheets, loading: worksheetsLoading, createWorksheet, updateWorksheet, updateWorksheetTimes, getTotalTime } = useWorksheets(
|
||||
const { worksheets, loading: worksheetsLoading, createWorksheet, updateWorksheet, updateWorksheetTimes, saveOwnSupport, getTotalTime } = useWorksheets(
|
||||
expanded ? ticket.woid : null
|
||||
)
|
||||
|
||||
@@ -144,6 +147,9 @@ export default function TicketCard({ ticket, onUpdate }) {
|
||||
loading={worksheetsLoading}
|
||||
canEditTimes={(ws) => canEditWorksheetTimes(ws, user, employee)}
|
||||
onEditTimes={setEditTimesWorksheet}
|
||||
canAddSupport={(ws) => canAddOwnSupport(ws, user, employee)}
|
||||
onOwnSupport={setOwnSupportWorksheet}
|
||||
currentUserId={user?.$id}
|
||||
onTogglePlan={handleTogglePlan}
|
||||
aboHoursAllowed={aboHoursAllowed}
|
||||
/>
|
||||
@@ -156,6 +162,14 @@ export default function TicketCard({ ticket, onUpdate }) {
|
||||
)}
|
||||
|
||||
<CreateWorksheetModal isOpen={showCreateWorksheet} onClose={() => setShowCreateWorksheet(false)} workorder={ticket} onCreate={handleCreateWorksheet} aboHoursAllowed={aboHoursAllowed} />
|
||||
{ownSupportWorksheet && (
|
||||
<OwnSupportModal
|
||||
worksheet={ownSupportWorksheet}
|
||||
currentUserId={user?.$id}
|
||||
onClose={() => setOwnSupportWorksheet(null)}
|
||||
onSave={(ws, task) => saveOwnSupport(ws, task, user, employee)}
|
||||
/>
|
||||
)}
|
||||
{editTimesWorksheet && (
|
||||
<WorksheetTimesModal
|
||||
worksheet={editTimesWorksheet}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useState } from 'react'
|
||||
import { FaClock, FaUser, FaExchangeAlt, FaComment, FaChevronDown, FaChevronUp, FaPen, FaCheck } from 'react-icons/fa'
|
||||
import { FaClock, FaUser, FaExchangeAlt, FaComment, FaChevronDown, FaChevronUp, FaPen, FaCheck, FaHandsHelping } from 'react-icons/fa'
|
||||
import { serviceTypeLabel, needsTimeEntry, worksheetTone } from '../lib/ticketForm'
|
||||
import { statusLabel } from '../lib/ticketStatus'
|
||||
import { parseSupporters } from '../lib/support'
|
||||
|
||||
// Kopffarbe je Zustand (worksheetTone): orange = Git-Zeit fehlt, gruen = Git-Zeit eingetragen
|
||||
const TONES = {
|
||||
@@ -24,7 +25,7 @@ const headerButtonStyle = {
|
||||
marginRight: '8px',
|
||||
}
|
||||
|
||||
export default function WorksheetList({ worksheets, totalTime, loading, onTogglePlan, aboHoursAllowed = true, canEditTimes, onEditTimes }) {
|
||||
export default function WorksheetList({ worksheets, totalTime, loading, onTogglePlan, aboHoursAllowed = true, canEditTimes, onEditTimes, canAddSupport, onOwnSupport, currentUserId }) {
|
||||
const [expandedWorksheets, setExpandedWorksheets] = useState({})
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -177,6 +178,23 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle
|
||||
: <><FaPen className="me-1" /> Zeiten bearbeiten</>}
|
||||
</button>
|
||||
)}
|
||||
{onOwnSupport && canAddSupport?.(ws) && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm"
|
||||
title="Eintragen, dass du bei dieser Arbeit unterstützt hast"
|
||||
style={headerButtonStyle}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onOwnSupport(ws)
|
||||
}}
|
||||
>
|
||||
<FaHandsHelping className="me-1" />{' '}
|
||||
{parseSupporters(ws.supporters).some((s) => s.employeeId === currentUserId)
|
||||
? 'Meine Unterstützung bearbeiten'
|
||||
: 'Ich habe unterstützt'}
|
||||
</button>
|
||||
)}
|
||||
{onTogglePlan && !ws.isComment && Number(ws.totalTime) > 0 && (aboHoursAllowed || ws.countsToPlan) && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from 'react'
|
||||
import { databases, DATABASE_ID, COLLECTIONS, Query, ID } from '../lib/appwrite'
|
||||
import { canEditWorksheetTimes, isGitWorksheet } from '../lib/ticketForm'
|
||||
import { roundUpToStep, isValidStep } from '../lib/timeSteps'
|
||||
import { parseSupporters, serializeSupporters } from '../lib/support'
|
||||
import { parseSupporters, serializeSupporters, canAddOwnSupport, upsertSupporter, removeSupporter } from '../lib/support'
|
||||
|
||||
const STEP_ERROR = 'Die Arbeitszeit muss in 15-Minuten-Schritten angegeben werden.'
|
||||
|
||||
@@ -258,6 +258,30 @@ export function useWorksheets(woid = null) {
|
||||
return updateWorksheet(worksheet.$id, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* "Ich habe unterstuetzt": eigenen Eintrag setzen (task) oder entfernen (task = null).
|
||||
* Liest das Blatt frisch, damit gleichzeitige Eintraege anderer erhalten bleiben.
|
||||
*/
|
||||
const saveOwnSupport = async (worksheet, task, currentUser, employee) => {
|
||||
if (!canAddOwnSupport(worksheet, currentUser, employee)) {
|
||||
return { success: false, error: 'Hier kannst du keine Unterstützung eintragen.' }
|
||||
}
|
||||
try {
|
||||
const fresh = await databases.getDocument(DATABASE_ID, COLLECTIONS.WORKSHEETS, worksheet.$id)
|
||||
const current = parseSupporters(fresh.supporters)
|
||||
const next = task === null
|
||||
? removeSupporter(current, currentUser.$id)
|
||||
: upsertSupporter(current, {
|
||||
employeeId: currentUser.$id,
|
||||
name: employee?.displayName || currentUser.name || '',
|
||||
task: String(task).trim().slice(0, 200)
|
||||
})
|
||||
return updateWorksheet(worksheet.$id, { supporters: serializeSupporters(next) })
|
||||
} catch (err) {
|
||||
return { success: false, error: err.message }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht ein Worksheet (sollte normalerweise nicht erlaubt sein - Audit Trail!)
|
||||
*/
|
||||
@@ -293,6 +317,7 @@ export function useWorksheets(woid = null) {
|
||||
createWorksheet,
|
||||
updateWorksheet,
|
||||
updateWorksheetTimes,
|
||||
saveOwnSupport,
|
||||
deleteWorksheet,
|
||||
refresh: fetchWorksheets,
|
||||
getTotalTime,
|
||||
|
||||
Reference in new issue
Block a user