map addition
This commit is contained in:
parent
c2c9ac39da
commit
b1003de515
@ -31,16 +31,23 @@ export interface ChartConfig {
|
||||
|
||||
export interface AnalyticsChartProps {
|
||||
data?: unknown[];
|
||||
gridCols?: number;
|
||||
}
|
||||
|
||||
export function AnalyticsChart({ data }: AnalyticsChartProps) {
|
||||
export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
|
||||
if (!data || !Array.isArray(data) || data.length === 0) return null;
|
||||
|
||||
const charts = data as ChartConfig[];
|
||||
const colors = ['#3B82F6', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899', '#14B8A6'];
|
||||
|
||||
const gridClass = gridCols === 1
|
||||
? "grid grid-cols-1 gap-6 w-full"
|
||||
: gridCols === 2
|
||||
? "grid grid-cols-1 lg:grid-cols-2 gap-6 w-full"
|
||||
: "grid grid-cols-1 lg:grid-cols-3 gap-6 w-full";
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 w-full">
|
||||
<div className={gridClass}>
|
||||
{charts.map((chart, idx) => {
|
||||
const title = (chart.key || `Chart ${idx + 1}`)
|
||||
.replace(/_/g, ' ')
|
||||
|
||||
@ -4,7 +4,7 @@ import { RecordView } from './RecordView';
|
||||
import type { WiredRecordViewProps } from './OrdersView';
|
||||
|
||||
/** Daily Logs record view (Daily Reports workflow). */
|
||||
export function DailyLogsView({ onRowClick, pageSize, headerActions, rowActions, refreshKey, presetAlias }: WiredRecordViewProps) {
|
||||
export function DailyLogsView({ onRowClick, pageSize, headerActions, rowActions, refreshKey, presetAlias, mapComponent }: WiredRecordViewProps) {
|
||||
return (
|
||||
<RecordView
|
||||
client={dailyReportsClient}
|
||||
@ -16,6 +16,7 @@ export function DailyLogsView({ onRowClick, pageSize, headerActions, rowActions,
|
||||
rowActions={rowActions}
|
||||
refreshKey={refreshKey}
|
||||
presetAlias={presetAlias}
|
||||
mapComponent={mapComponent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ export interface WiredRecordViewProps {
|
||||
refreshKey?: number;
|
||||
initialFilters?: Record<string, string>;
|
||||
presetAlias?: string;
|
||||
mapComponent?: React.ReactNode;
|
||||
}
|
||||
|
||||
/** Orders record view (Order Booking workflow). */
|
||||
|
||||
@ -43,6 +43,8 @@ export interface RecordViewProps {
|
||||
initialFilters?: Record<string, string>;
|
||||
/** Custom preset alias to send with the request (e.g. preset_alias: my_orders) */
|
||||
presetAlias?: string;
|
||||
/** Component to render next to charts (e.g., a map) */
|
||||
mapComponent?: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,6 +68,7 @@ export function RecordView({
|
||||
omitColumns,
|
||||
initialFilters = {},
|
||||
presetAlias,
|
||||
mapComponent,
|
||||
}: RecordViewProps) {
|
||||
const [internalPageSize, setInternalPageSize] = useState(pageSize);
|
||||
const [page, setPage] = useState(1);
|
||||
@ -150,12 +153,26 @@ export function RecordView({
|
||||
const tileValues = resp?.tile_values;
|
||||
|
||||
const chartData = resp?.chart_data;
|
||||
const hasChart = chartData && Array.isArray(chartData) && chartData.length > 0;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 w-full">
|
||||
<StatsTiles tiles={tileValues} />
|
||||
|
||||
<AnalyticsChart data={chartData} />
|
||||
{(hasChart || mapComponent) ? (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 w-full">
|
||||
{hasChart && (
|
||||
<div className={mapComponent ? "lg:col-span-1" : "lg:col-span-3"}>
|
||||
<AnalyticsChart data={chartData} gridCols={mapComponent ? 1 : undefined} />
|
||||
</div>
|
||||
)}
|
||||
{mapComponent && (
|
||||
<div className={hasChart ? "lg:col-span-2" : "lg:col-span-3"}>
|
||||
{mapComponent}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Card
|
||||
title={title}
|
||||
|
||||
@ -4,7 +4,7 @@ import { RecordView } from './RecordView';
|
||||
import type { WiredRecordViewProps } from './OrdersView';
|
||||
|
||||
/** Store record view (Store workflow). */
|
||||
export function StoresView({ onRowClick, pageSize, headerActions, rowActions, refreshKey }: WiredRecordViewProps & { headerActions?: React.ReactNode }) {
|
||||
export function StoresView({ onRowClick, pageSize, headerActions, rowActions, refreshKey, mapComponent }: WiredRecordViewProps & { headerActions?: React.ReactNode }) {
|
||||
return (
|
||||
<RecordView
|
||||
client={storeClient}
|
||||
@ -15,6 +15,7 @@ export function StoresView({ onRowClick, pageSize, headerActions, rowActions, re
|
||||
headerActions={headerActions}
|
||||
rowActions={rowActions}
|
||||
refreshKey={refreshKey}
|
||||
mapComponent={mapComponent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { DailyLogsView } from '../components/rv';
|
||||
import { DailyLogDetail } from '../components/dv';
|
||||
import { useState } from 'react';
|
||||
import { Button } from '../components/buttons/Button';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Plus, MapPin } from 'lucide-react';
|
||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||
import { DAILY_REPORTS } from '../api/config';
|
||||
import { dailyReportsClient } from '../api/clients';
|
||||
@ -19,10 +19,31 @@ export function DailyLogsPage() {
|
||||
const [punchOutTitle, setPunchOutTitle] = useState('Punch Out');
|
||||
const [refreshKey, setRefreshKey] = useState(0);
|
||||
|
||||
const mapComponent = (
|
||||
<div className="bg-white rounded-2xl shadow-sm border border-slate-100 overflow-hidden flex flex-col h-full min-h-[320px]">
|
||||
<div className="p-4 border-b border-slate-100 flex items-center gap-2">
|
||||
<MapPin size={18} className="text-slate-400" />
|
||||
<h3 className="text-slate-800 font-bold">Activity Locations</h3>
|
||||
</div>
|
||||
<div className="flex-1 relative bg-slate-50">
|
||||
<iframe
|
||||
title="Logs Map"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{ border: 0, position: 'absolute', inset: 0 }}
|
||||
loading="lazy"
|
||||
allowFullScreen
|
||||
src="https://maps.google.com/maps?q=activity&hl=en&z=10&output=embed"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{instanceId == null && (
|
||||
<DailyLogsView
|
||||
mapComponent={mapComponent}
|
||||
refreshKey={refreshKey}
|
||||
presetAlias="my_logs"
|
||||
onRowClick={(row) => {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { StoresView } from '../components/rv';
|
||||
import { StoreDetail } from '../components/dv';
|
||||
import { Button } from '../components/buttons/Button';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Plus, MapPin } from 'lucide-react';
|
||||
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||
import { STORE } from '../api/config';
|
||||
import { storeClient } from '../api/clients';
|
||||
@ -55,9 +55,30 @@ export function StoresPage() {
|
||||
);
|
||||
}
|
||||
|
||||
const mapComponent = (
|
||||
<div className="bg-white rounded-2xl shadow-sm border border-slate-100 overflow-hidden flex flex-col h-full min-h-[320px]">
|
||||
<div className="p-4 border-b border-slate-100 flex items-center gap-2">
|
||||
<MapPin size={18} className="text-slate-400" />
|
||||
<h3 className="text-slate-800 font-bold">Location Spread</h3>
|
||||
</div>
|
||||
<div className="flex-1 relative bg-slate-50">
|
||||
<iframe
|
||||
title="Stores Map"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{ border: 0, position: 'absolute', inset: 0 }}
|
||||
loading="lazy"
|
||||
allowFullScreen
|
||||
src="https://maps.google.com/maps?q=stores&hl=en&z=10&output=embed"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StoresView
|
||||
mapComponent={mapComponent}
|
||||
refreshKey={refreshKey}
|
||||
onRowClick={(row) => {
|
||||
const id = row.instance_id as number | string | undefined;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user