fix: premjesti kalendar widget u Layout, popravi PDF toast i Servisi danas
- TaskCalendarPortal u Layout.astro (van AnimatedPage transforma) pa fixed pozicija uvijek prati viewport rub bez probijanja sadržaja - Kalendar widgeta panel koristi clipping wrapper (overflow:hidden + animirana width) umjesto translate3d — potpuno skriva sjenu i border kad je zatvoren - Serviser vidi samo svoje taskove, supervisor vidi sve; neprijavljeni korisnik ne vidi widget (return null) - FleetDashboardShell sluša calendar:open-task event i otvara TaskServiceRecordsModal - Servisi danas broji Taskove (scheduled_date=danas, status aktivan/servis) umjesto Servisnih zapisa - downloadGeneratedPdfByUrl hvata grešku i prikazuje Toast (npr. PDF nije dostupan ili je istekao) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -6,7 +6,6 @@ import WorkOrderModal from './WorkOrderModal';
|
||||
import TaskCreateModal from './TaskCreateModal';
|
||||
import TaskServiceRecordsModal from './TaskServiceRecordsModal';
|
||||
import WorkOrderDetailModal from './WorkOrderDetailModal';
|
||||
import TaskCalendarWidget from './TaskCalendarWidget';
|
||||
import SyncLogPanel from './SyncLogPanel';
|
||||
import ServiceRecordForm from '../fleet/ServiceRecordForm';
|
||||
import ServicePhotoUpload from '../fleet/ServicePhotoUpload';
|
||||
@@ -145,6 +144,15 @@ export default function FleetDashboardShell({ initialSection = 'dashboard', page
|
||||
hydrateServiceContext();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
function onCalendarOpenTask(event) {
|
||||
const task = event?.detail?.task;
|
||||
if (task) setSelectedTask(task);
|
||||
}
|
||||
window.addEventListener('calendar:open-task', onCalendarOpenTask);
|
||||
return () => window.removeEventListener('calendar:open-task', onCalendarOpenTask);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const nextSection = String(resolvedInitialSection || 'dashboard');
|
||||
setActiveSection(nextSection);
|
||||
@@ -593,12 +601,6 @@ export default function FleetDashboardShell({ initialSection = 'dashboard', page
|
||||
|
||||
return (
|
||||
<AnimatedPage className="min-h-screen">
|
||||
{hasMounted && (
|
||||
<TaskCalendarWidget
|
||||
tasks={tasks}
|
||||
onTaskClick={(task) => setSelectedTask(task)}
|
||||
/>
|
||||
)}
|
||||
<main className="min-w-0">
|
||||
<DashboardTopbar
|
||||
activeSection={activeSection}
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function TaskCalendarWidget({ tasks = [], onTaskClick }) {
|
||||
const [selectedDay, setSelectedDay] = useState(null);
|
||||
|
||||
const panelSpring = useSpring({
|
||||
transform: open ? 'translate3d(0%,0,0)' : `translate3d(-100%,0,0)`,
|
||||
left: open ? 0 : -PANEL_WIDTH,
|
||||
config: { tension: 260, friction: 28 },
|
||||
});
|
||||
|
||||
@@ -99,11 +99,19 @@ export default function TaskCalendarWidget({ tasks = [], onTaskClick }) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Sliding panel */}
|
||||
{/* Clipping wrapper — animates width 0→300, clips shadow+border completely */}
|
||||
<animated.div
|
||||
style={{ ...panelSpring, width: `${PANEL_WIDTH}px` }}
|
||||
className="fixed left-0 top-0 z-[46] flex h-screen flex-col overflow-hidden border-r border-border-hairline bg-canvas-elevated shadow-2xl"
|
||||
style={{
|
||||
width: panelSpring.left.to((v) => `${Math.max(0, v + PANEL_WIDTH)}px`),
|
||||
pointerEvents: open ? 'auto' : 'none',
|
||||
}}
|
||||
className="fixed left-0 top-0 z-[46] h-screen overflow-hidden"
|
||||
>
|
||||
{/* Inner panel — always 300px, anchored to right edge of wrapper */}
|
||||
<div
|
||||
style={{ width: `${PANEL_WIDTH}px` }}
|
||||
className="absolute right-0 top-0 flex h-full flex-col border-r border-border-hairline bg-canvas-elevated shadow-2xl"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between border-b border-border-hairline px-4 py-3">
|
||||
<h2 className="text-sm font-semibold text-text-main">📅 Kalendar zadataka</h2>
|
||||
@@ -244,6 +252,7 @@ export default function TaskCalendarWidget({ tasks = [], onTaskClick }) {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</animated.div>
|
||||
|
||||
{/* Toggle tab button — slides right as panel opens */}
|
||||
|
||||
50
frontend/src/components/layout/TaskCalendarPortal.jsx
Normal file
50
frontend/src/components/layout/TaskCalendarPortal.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useMemo } from 'preact/hooks';
|
||||
import { useStore } from '@nanostores/preact';
|
||||
import { $tasks } from '../../stores/taskStore';
|
||||
import { $user } from '../../stores/authStore';
|
||||
import TaskCalendarWidget from '../dashboard/TaskCalendarWidget';
|
||||
|
||||
/**
|
||||
* Mountan u Layout.astro (van AnimatedPage transforma) pa fixed pozicija
|
||||
* uvijek ostaje uz viewport rub.
|
||||
*
|
||||
* Filtriranje taskova po ulozi:
|
||||
* - Serviser → samo taskovi dodijeljeni njemu
|
||||
* - Supervisor (nije serviser) → svi taskovi
|
||||
*
|
||||
* Kad korisnik klikne task u kalendaru, šaljemo custom event koji
|
||||
* FleetDashboardShell sluša i otvara TaskServiceRecordsModal.
|
||||
*/
|
||||
export default function TaskCalendarPortal() {
|
||||
const allTasks = useStore($tasks);
|
||||
const user = useStore($user);
|
||||
|
||||
const isServiser = Boolean(user?.is_serviser);
|
||||
const isTeamMember = Boolean(user?.is_team_member);
|
||||
const isSupervisorNotServiser = isTeamMember && !isServiser;
|
||||
|
||||
const tasks = useMemo(() => {
|
||||
if (!user?.id) return [];
|
||||
// Supervisor vidi sve; serviser vidi samo svoje
|
||||
if (isSupervisorNotServiser) return allTasks;
|
||||
return allTasks.filter(
|
||||
(t) => String(t.assigned_to || '') === String(user.id)
|
||||
);
|
||||
}, [allTasks, user, isSupervisorNotServiser]);
|
||||
|
||||
// Ne renderiramo ništa ako korisnik nije prijavljen
|
||||
if (!user?.id) return null;
|
||||
|
||||
function handleTaskClick(task) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('calendar:open-task', { detail: { task } })
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TaskCalendarWidget
|
||||
tasks={tasks}
|
||||
onTaskClick={handleTaskClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user