added maps to the detail view
This commit is contained in:
parent
7d6f1a9021
commit
fdfa5e386b
@ -4,18 +4,204 @@ import type { WiredDetailViewProps } from './OrderDetail';
|
|||||||
import { useDetailViewData } from './useDetailViewData';
|
import { useDetailViewData } from './useDetailViewData';
|
||||||
import { Spinner } from '../reusable/Spinner';
|
import { Spinner } from '../reusable/Spinner';
|
||||||
import { EmptyState } from '../reusable/EmptyState';
|
import { EmptyState } from '../reusable/EmptyState';
|
||||||
import { DailyLogCard } from '../cards/DailyLogCard';
|
import { formatValue } from '../../lib/format';
|
||||||
|
import { ArrowLeft, MapPin, Clock, FileText } from 'lucide-react';
|
||||||
|
import { NearestStoresMap } from '../maps/NearestStoresMap';
|
||||||
|
|
||||||
export function DailyLogDetail({ instanceId }: WiredDetailViewProps) {
|
export interface DailyLogDetailProps extends WiredDetailViewProps {
|
||||||
|
onBack?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DailyLogDetail({ instanceId, onBack }: DailyLogDetailProps) {
|
||||||
const { data, loading, error } = useDetailViewData(dailyReportsClient, DAILY_REPORTS.detailViews.DAILY_LOGS, instanceId);
|
const { data, loading, error } = useDetailViewData(dailyReportsClient, DAILY_REPORTS.detailViews.DAILY_LOGS, instanceId);
|
||||||
|
|
||||||
if (error) return <EmptyState title="Couldn't load record" hint={error} />;
|
if (error) {
|
||||||
if (loading) return <div className="py-8 flex justify-center"><Spinner label="Loading Daily Log..." /></div>;
|
return (
|
||||||
|
<div className="bg-[var(--tiles-card-bg)] p-8 rounded-2xl border border-border-default shadow-sm text-center">
|
||||||
|
<EmptyState title="Couldn't load record" hint={error} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="bg-[var(--tiles-card-bg)] p-12 rounded-2xl border border-border-default shadow-sm flex justify-center items-center">
|
||||||
|
<Spinner label="Loading daily log..." />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!data) return <EmptyState title="No details found" />;
|
if (!data) return <EmptyState title="No details found" />;
|
||||||
|
|
||||||
|
const row = data;
|
||||||
|
const stateName = String(row.status || row.current_state_name || 'Logged');
|
||||||
|
|
||||||
|
// User details
|
||||||
|
const userObj = (row.sales_officer_name || row.user_id || row.user) as Record<string, any> | null;
|
||||||
|
const userName = formatValue(userObj?.name || row.user_name || 'Unknown User');
|
||||||
|
const userEmail = formatValue(userObj?.email || row.performed_by_email || row.user_email || '—');
|
||||||
|
const initials = userName !== '—' ? String(userName).substring(0, 2).toUpperCase() : 'SO';
|
||||||
|
|
||||||
|
// Route Details
|
||||||
|
const routeCode = formatValue(row.route_code || row.route || '—');
|
||||||
|
const subRoute = formatValue(row.sub_route || row.area || '—');
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
const prodCalls = formatValue(row.total_productive_calls || 0);
|
||||||
|
const nonProdCalls = formatValue(row.total_non_productive_calls || 0);
|
||||||
|
const eodNotes = formatValue(row.eod_notes_remarks || row.notes || '—');
|
||||||
|
|
||||||
|
// Format Date & Time
|
||||||
|
let dateStr = '—';
|
||||||
|
if (row.date) dateStr = String(row.date).split('T')[0];
|
||||||
|
else if (row.created_at) dateStr = String(row.created_at).split('T')[0];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (dateStr !== '—') {
|
||||||
|
const d = new Date(dateStr);
|
||||||
|
if (!isNaN(d.getTime())) {
|
||||||
|
dateStr = d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }).replace(/ /g, '-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const formatTimeStr = (t: string | undefined | null) => {
|
||||||
|
if (!t) return '—';
|
||||||
|
const str = String(t);
|
||||||
|
if (str === '—') return str;
|
||||||
|
try {
|
||||||
|
let timeParts = str.split(':');
|
||||||
|
if (str.includes('T')) {
|
||||||
|
timeParts = str.split('T')[1].replace('Z', '').split('.')[0].split(':');
|
||||||
|
}
|
||||||
|
if (timeParts.length >= 2) {
|
||||||
|
let h = parseInt(timeParts[0]);
|
||||||
|
const m = timeParts[1];
|
||||||
|
const ampm = h >= 12 ? 'PM' : 'AM';
|
||||||
|
h = h % 12;
|
||||||
|
h = h ? h : 12;
|
||||||
|
const hStr = h < 10 ? '0' + h : h;
|
||||||
|
return `${hStr}:${m} ${ampm}`;
|
||||||
|
}
|
||||||
|
return str.split('.')[0];
|
||||||
|
} catch {
|
||||||
|
return str.split('.')[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkInTimeRaw = row.time || row.created_at || row.punch_in_time;
|
||||||
|
const checkInTime = formatTimeStr(checkInTimeRaw);
|
||||||
|
|
||||||
|
let checkOutTimeRaw = row.check_out_time || row.punch_out_time;
|
||||||
|
if (!checkOutTimeRaw && stateName.toLowerCase().includes('out') && row.time) {
|
||||||
|
checkOutTimeRaw = row.time;
|
||||||
|
}
|
||||||
|
const checkOutTime = checkOutTimeRaw ? formatTimeStr(checkOutTimeRaw) : null;
|
||||||
|
|
||||||
|
const isPunchedIn = stateName.toLowerCase().includes('punched in') || stateName.toLowerCase().includes('active') || (stateName.toLowerCase().includes('in') && !stateName.toLowerCase().includes('out'));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="text-slate-800 py-6 lg:py-8 font-sans space-y-6">
|
||||||
<DailyLogCard row={data} isDetailView={true} />
|
<div className="bg-[var(--tiles-card-bg)] rounded-2xl border border-slate-200/80 p-6 shadow-sm space-y-6">
|
||||||
|
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
{onBack && (
|
||||||
|
<button onClick={onBack} className="mt-1 p-1.5 sm:p-2 text-slate-800 hover:text-slate-800 hover:bg-slate-100/80 rounded-full transition-colors shrink-0 -ml-2">
|
||||||
|
<ArrowLeft size={20} strokeWidth={2.5} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<div className="w-12 h-12 rounded-full bg-slate-200 text-slate-700 flex items-center justify-center font-bold text-lg shrink-0">
|
||||||
|
{initials}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex flex-wrap items-center gap-2 text-xs mb-1">
|
||||||
|
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold ${
|
||||||
|
isPunchedIn ? 'bg-emerald-50 text-emerald-700 border border-emerald-200' : 'bg-blue-50 text-blue-700 border border-blue-200'
|
||||||
|
}`}>
|
||||||
|
{stateName}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-xl font-bold text-slate-900 tracking-tight">
|
||||||
|
{userName}
|
||||||
|
</h1>
|
||||||
|
{userEmail !== '—' && (
|
||||||
|
<div className="text-sm font-medium text-slate-500">{userEmail}</div>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-wrap items-center gap-3 text-xs text-slate-800 font-medium pt-1">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<MapPin size={13} className="text-slate-800" /> {routeCode}
|
||||||
|
</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>{subRoute}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-nowrap items-center gap-3 w-full lg:w-auto overflow-x-auto pb-2 min-w-0">
|
||||||
|
<div className="bg-primary-light-bg border border-primary-light-border rounded-2xl p-3 text-center min-w-[100px] flex-1">
|
||||||
|
<div className="text-[10px] font-bold text-primary uppercase tracking-wider mb-0.5">PROD. CALLS</div>
|
||||||
|
<div className="text-[20px] font-extrabold text-primary">{prodCalls}</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-primary-light-bg border border-primary-light-border rounded-2xl p-3 text-center min-w-[100px] flex-1">
|
||||||
|
<div className="text-[10px] font-bold text-primary uppercase tracking-wider mb-0.5">NON PROD.</div>
|
||||||
|
<div className="text-[20px] font-extrabold text-primary">{nonProdCalls}</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-primary-light-bg border border-primary-light-border rounded-2xl p-3 text-center min-w-[110px] flex-1">
|
||||||
|
<div className="text-[10px] font-bold text-primary uppercase tracking-wider mb-0.5">TOTAL CALLS</div>
|
||||||
|
<div className="text-sm font-bold text-primary mt-1 whitespace-nowrap overflow-hidden text-ellipsis">
|
||||||
|
{Number(prodCalls) + Number(nonProdCalls)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-slate-100 pt-4">
|
||||||
|
<div className="flex items-center gap-8 overflow-x-auto pb-2">
|
||||||
|
<div className="flex items-center gap-3 min-w-max">
|
||||||
|
<div className="w-8 h-8 rounded-full bg-emerald-50 text-emerald-600 flex items-center justify-center text-xs shrink-0">
|
||||||
|
<Clock size={16} />
|
||||||
|
</div>
|
||||||
|
<div className="text-xs">
|
||||||
|
<div className="font-bold text-slate-800 uppercase tracking-wider text-[10px] mb-0.5">PUNCH IN</div>
|
||||||
|
<div className="text-slate-800 font-medium text-[12px] leading-tight">
|
||||||
|
{dateStr} • {checkInTime}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{checkOutTime && (
|
||||||
|
<div className="flex items-center gap-3 min-w-max">
|
||||||
|
<div className="w-8 h-8 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center text-xs shrink-0">
|
||||||
|
<Clock size={16} />
|
||||||
|
</div>
|
||||||
|
<div className="text-xs">
|
||||||
|
<div className="font-bold text-slate-800 uppercase tracking-wider text-[10px] mb-0.5">PUNCH OUT</div>
|
||||||
|
<div className="text-slate-800 font-medium text-[12px] leading-tight">
|
||||||
|
{dateStr} • {checkOutTime}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!isPunchedIn && eodNotes !== '—' && (
|
||||||
|
<div className="bg-[var(--tiles-card-bg)] rounded-2xl border border-slate-200/80 p-6 shadow-sm">
|
||||||
|
<div className="flex items-center gap-2 mb-3 text-slate-800">
|
||||||
|
<FileText size={18} className="text-slate-500" />
|
||||||
|
<h3 className="font-bold text-lg">EOD Notes</h3>
|
||||||
|
</div>
|
||||||
|
<div className="bg-slate-50 p-4 rounded-xl border border-slate-100 text-sm text-slate-700 leading-relaxed whitespace-pre-wrap">
|
||||||
|
{eodNotes}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Embed Map at the bottom of the detail view */}
|
||||||
|
<div className="w-full overflow-hidden">
|
||||||
|
<NearestStoresMap />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -628,6 +628,21 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'app_user') {
|
||||||
|
const user = client.currentUser();
|
||||||
|
const displayVal = user?.name || val;
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
label={f.name}
|
||||||
|
required={f.mandatory}
|
||||||
|
type="text"
|
||||||
|
value={(displayVal as string) ?? ''}
|
||||||
|
onChange={() => {}}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
label={f.name}
|
label={f.name}
|
||||||
|
|||||||
@ -6,12 +6,16 @@ export function TextField({
|
|||||||
required,
|
required,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
disabled,
|
||||||
|
readOnly,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
type: string;
|
type: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (val: string) => void;
|
onChange: (val: string) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
readOnly?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
@ -20,6 +24,8 @@ export function TextField({
|
|||||||
type={type === 'number' ? 'number' : type === 'email' ? 'email' : 'text'}
|
type={type === 'number' ? 'number' : type === 'email' ? 'email' : 'text'}
|
||||||
value={value ?? ''}
|
value={value ?? ''}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
|
readOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useJsApiLoader, GoogleMap, Marker, InfoWindow } from '@react-google-maps/api';
|
import { useJsApiLoader, GoogleMap, Marker, InfoWindow } from '@react-google-maps/api';
|
||||||
import { PIPELINE, BASE_URL } from '../../api/config';
|
import { PIPELINE, BASE_URL } from '../../api/config';
|
||||||
import { Loader2, MapPin } from 'lucide-react';
|
import { Loader2, MapPin, ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
|
|
||||||
// Interfaces for the API response
|
// Interfaces for the API response
|
||||||
interface StoreLocation {
|
interface StoreLocation {
|
||||||
@ -42,6 +42,7 @@ export function NearestStoresMap() {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [selectedStore, setSelectedStore] = useState<Store | null>(null);
|
const [selectedStore, setSelectedStore] = useState<Store | null>(null);
|
||||||
|
const [isListExpanded, setIsListExpanded] = useState(false);
|
||||||
|
|
||||||
const isLocalhost = typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
|
const isLocalhost = typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ export function NearestStoresMap() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="w-full h-[50vh] min-h-[350px] relative px-4 pt-4">
|
<div className="w-full h-[50vh] min-h-[350px] relative pt-4">
|
||||||
<GoogleMap
|
<GoogleMap
|
||||||
mapContainerStyle={mapContainerStyle}
|
mapContainerStyle={mapContainerStyle}
|
||||||
center={userLocation || defaultCenter}
|
center={userLocation || defaultCenter}
|
||||||
@ -203,35 +204,44 @@ export function NearestStoresMap() {
|
|||||||
</GoogleMap>
|
</GoogleMap>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx-4 mt-4 mb-4 bg-card rounded-2xl shadow-[0_1px_0_0_rgba(0,0,0,0.02),0_20px_40px_-24px_rgba(20,80,60,0.15)] p-5 flex flex-col gap-4">
|
<div className="mt-4 mb-4 bg-card rounded-2xl shadow-[0_1px_0_0_rgba(0,0,0,0.02),0_20px_40px_-24px_rgba(20,80,60,0.15)] p-5 flex flex-col gap-4">
|
||||||
<h3 className="font-bold text-[17px] text-strong flex items-center gap-2">
|
<div className="flex justify-between items-center cursor-pointer" onClick={() => setIsListExpanded(!isListExpanded)}>
|
||||||
<MapPin className="w-5 h-5 text-primary" />
|
<h3 className="font-bold text-[17px] text-strong flex items-center gap-2">
|
||||||
Nearest Stores ({stores.length})
|
<MapPin className="w-5 h-5 text-primary" />
|
||||||
</h3>
|
Nearest Stores ({stores.length})
|
||||||
{loading && stores.length === 0 && (
|
</h3>
|
||||||
<div className="py-4 text-center text-sm text-slate-500">
|
<button className="text-slate-400 hover:text-slate-600 transition-colors p-1">
|
||||||
Fetching nearby stores...
|
{isListExpanded ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
|
||||||
</div>
|
</button>
|
||||||
)}
|
|
||||||
{!loading && stores.length === 0 && userLocation && (
|
|
||||||
<div className="py-4 text-center text-sm text-slate-500">
|
|
||||||
No stores found nearby.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
{stores.map(store => (
|
|
||||||
<div key={store.store_code} className="z-card !p-3 hover:!border-primary/40 cursor-pointer" onClick={() => setSelectedStore(store)}>
|
|
||||||
<div className="flex justify-between items-start mb-1">
|
|
||||||
<span className="font-bold text-[17px] text-strong line-clamp-1">{store.business_name}</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-muted line-clamp-1">{store.area}</p>
|
|
||||||
<div className="flex justify-between items-center mt-3 pt-3 border-t border-slate-200/60">
|
|
||||||
<span className="text-xs font-semibold text-primary uppercase tracking-wider">{store.route_name}</span>
|
|
||||||
<span className="text-xs font-bold text-strong">{store.distance_km} km</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
{isListExpanded && (
|
||||||
|
<>
|
||||||
|
{loading && stores.length === 0 && (
|
||||||
|
<div className="py-4 text-center text-sm text-slate-500">
|
||||||
|
Fetching nearby stores...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!loading && stores.length === 0 && userLocation && (
|
||||||
|
<div className="py-4 text-center text-sm text-slate-500">
|
||||||
|
No stores found nearby.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
{stores.map(store => (
|
||||||
|
<div key={store.store_code} className="z-card !p-3 hover:!border-primary/40 cursor-pointer" onClick={() => setSelectedStore(store)}>
|
||||||
|
<div className="flex justify-between items-start mb-1">
|
||||||
|
<span className="font-bold text-[17px] text-strong line-clamp-1">{store.business_name}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted line-clamp-1">{store.area}</p>
|
||||||
|
<div className="flex justify-between items-center mt-3 pt-3 border-t border-slate-200/60">
|
||||||
|
<span className="text-xs font-semibold text-primary uppercase tracking-wider">{store.route_name}</span>
|
||||||
|
<span className="text-xs font-bold text-strong">{store.distance_km} km</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { ClipboardList } from 'lucide-react';
|
|||||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||||
import { DAILY_REPORTS } from '../api/config';
|
import { DAILY_REPORTS } from '../api/config';
|
||||||
import { dailyReportsClient } from '../api/clients';
|
import { dailyReportsClient } from '../api/clients';
|
||||||
import { NearestStoresMap } from '../components/maps/NearestStoresMap';
|
|
||||||
|
|
||||||
export function DailyLogsPage() {
|
export function DailyLogsPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@ -20,10 +20,20 @@ export function DailyLogsPage() {
|
|||||||
const [punchOutTitle, setPunchOutTitle] = useState("Punch Out");
|
const [punchOutTitle, setPunchOutTitle] = useState("Punch Out");
|
||||||
const [refreshKey, setRefreshKey] = useState(0);
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
|
|
||||||
|
if (instanceId != null) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full bg-transparent w-full relative">
|
||||||
|
<div className="flex-1 pb-24 w-full overflow-y-auto">
|
||||||
|
<DailyLogDetail instanceId={instanceId} onBack={() => navigate(`/daily`)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-transparent w-full relative">
|
<div className="flex flex-col h-full bg-transparent w-full relative">
|
||||||
<div className="flex-1 overflow-y-auto pb-24">
|
<div className="flex-1 overflow-y-auto pb-24">
|
||||||
<NearestStoresMap />
|
|
||||||
|
|
||||||
<DailyLogsView
|
<DailyLogsView
|
||||||
hideTiles
|
hideTiles
|
||||||
@ -85,15 +95,6 @@ export function DailyLogsPage() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal
|
|
||||||
open={instanceId != null}
|
|
||||||
onClose={() => navigate(`/daily`)}
|
|
||||||
title={instanceId != null ? `Daily Log #${instanceId}` : undefined}
|
|
||||||
width="lg"
|
|
||||||
>
|
|
||||||
{instanceId != null && <DailyLogDetail instanceId={instanceId} />}
|
|
||||||
</Modal>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user