added all logs page and presetalias added to mylogs
This commit is contained in:
parent
9ee67313f5
commit
9e423f0730
@ -6,6 +6,7 @@ import { OrdersPage } from './screens/OrdersPage'
|
|||||||
import { CallsPage } from './screens/CallsPage'
|
import { CallsPage } from './screens/CallsPage'
|
||||||
import { StoresPage } from './screens/StoresPage'
|
import { StoresPage } from './screens/StoresPage'
|
||||||
import { DailyLogsPage } from './screens/DailyLogsPage'
|
import { DailyLogsPage } from './screens/DailyLogsPage'
|
||||||
|
import { AllDailyLogsPage } from './screens/AllDailyLogsPage'
|
||||||
import { AnalyticsPage } from './screens/AnalyticsPage'
|
import { AnalyticsPage } from './screens/AnalyticsPage'
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@ -26,9 +27,13 @@ function App() {
|
|||||||
<Route path="/daily" element={<DailyLogsPage />} />
|
<Route path="/daily" element={<DailyLogsPage />} />
|
||||||
<Route path="/daily/:instanceId" element={<DailyLogsPage />} />
|
<Route path="/daily/:instanceId" element={<DailyLogsPage />} />
|
||||||
|
|
||||||
|
<Route path="/all-daily" element={<AllDailyLogsPage />} />
|
||||||
|
<Route path="/all-daily/:instanceId" element={<AllDailyLogsPage />} />
|
||||||
|
|
||||||
|
|
||||||
<Route path="/analytics" element={<AnalyticsPage />} />
|
<Route path="/analytics" element={<AnalyticsPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="*" element={<Navigate to="/orders" replace />} />
|
<Route path="*" element={<Navigate to="/daily" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
|
|||||||
@ -146,6 +146,7 @@ export class ZinoClient {
|
|||||||
recordView(rvUid: string, params: RecordViewParams = {}): Promise<RecordViewResponse> {
|
recordView(rvUid: string, params: RecordViewParams = {}): Promise<RecordViewResponse> {
|
||||||
return this.request<RecordViewResponse>('POST', `/app/${APP_ID}/view/recordview`, {
|
return this.request<RecordViewResponse>('POST', `/app/${APP_ID}/view/recordview`, {
|
||||||
rv_template_uid: rvUid,
|
rv_template_uid: rvUid,
|
||||||
|
preset_alias: params.preset_alias,
|
||||||
search_query: {
|
search_query: {
|
||||||
page: params.page ?? 1,
|
page: params.page ?? 1,
|
||||||
limit: params.limit ?? 50,
|
limit: params.limit ?? 50,
|
||||||
|
|||||||
@ -53,6 +53,7 @@ export interface RecordViewParams {
|
|||||||
sortDir?: 'asc' | 'desc';
|
sortDir?: 'asc' | 'desc';
|
||||||
search?: string;
|
search?: string;
|
||||||
filters?: Array<{ field_key: string; value: string; data_type?: string }>;
|
filters?: Array<{ field_key: string; value: string; data_type?: string }>;
|
||||||
|
preset_alias?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Form schema (POST /app/{appId}/view/form-screens) ---
|
// --- Form schema (POST /app/{appId}/view/form-screens) ---
|
||||||
|
|||||||
@ -5,16 +5,17 @@ import type { WiredRecordViewProps } from './OrdersView';
|
|||||||
import { DailyLogCard } from '../cards/DailyLogCard';
|
import { DailyLogCard } from '../cards/DailyLogCard';
|
||||||
|
|
||||||
/** Daily Logs record view (Daily Reports workflow). */
|
/** Daily Logs record view (Daily Reports workflow). */
|
||||||
export function DailyLogsView({ onRowClick, onPunchOutRow, pageSize, headerActions, refreshKey }: WiredRecordViewProps & { onPunchOutRow?: (row: any) => void }) {
|
export function DailyLogsView({ onRowClick, onPunchOutRow, pageSize, headerActions, refreshKey, preset_alias = "my_logs", title = "Daily Logs" }: WiredRecordViewProps & { onPunchOutRow?: (row: any) => void, preset_alias?: string, title?: string }) {
|
||||||
return (
|
return (
|
||||||
<RecordView
|
<RecordView
|
||||||
client={dailyReportsClient}
|
client={dailyReportsClient}
|
||||||
rvUid={DAILY_REPORTS.recordViews.DAILY_LOGS}
|
rvUid={DAILY_REPORTS.recordViews.DAILY_LOGS}
|
||||||
title="Daily Logs"
|
title={title}
|
||||||
onRowClick={onRowClick}
|
onRowClick={onRowClick}
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
headerActions={headerActions}
|
headerActions={headerActions}
|
||||||
refreshKey={refreshKey}
|
refreshKey={refreshKey}
|
||||||
|
preset_alias={preset_alias}
|
||||||
sortBy="instance_id"
|
sortBy="instance_id"
|
||||||
sortDir="desc"
|
sortDir="desc"
|
||||||
hideChart
|
hideChart
|
||||||
|
|||||||
@ -42,6 +42,8 @@ export interface RecordViewProps {
|
|||||||
sortDir?: 'asc' | 'desc';
|
sortDir?: 'asc' | 'desc';
|
||||||
/** If true, the analytics chart will not be rendered even if data is returned */
|
/** If true, the analytics chart will not be rendered even if data is returned */
|
||||||
hideChart?: boolean;
|
hideChart?: boolean;
|
||||||
|
/** Pass a preset alias to apply server-side presets */
|
||||||
|
preset_alias?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,6 +66,7 @@ export function RecordView({
|
|||||||
sortBy,
|
sortBy,
|
||||||
sortDir,
|
sortDir,
|
||||||
hideChart = false,
|
hideChart = false,
|
||||||
|
preset_alias,
|
||||||
}: RecordViewProps) {
|
}: RecordViewProps) {
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
@ -104,7 +107,7 @@ export function RecordView({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const r = await client.recordView(rvUid, { page, limit: pageSize, search: debounced, filters: filtersParam, sortBy, sortDir });
|
const r = await client.recordView(rvUid, { preset_alias, page, limit: pageSize, search: debounced, filters: filtersParam, sortBy, sortDir });
|
||||||
if (live) setResp(r);
|
if (live) setResp(r);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (live) setError((e as { message?: string })?.message ?? 'Failed to load');
|
if (live) setError((e as { message?: string })?.message ?? 'Failed to load');
|
||||||
@ -116,7 +119,7 @@ export function RecordView({
|
|||||||
return () => {
|
return () => {
|
||||||
live = false;
|
live = false;
|
||||||
};
|
};
|
||||||
}, [client, rvUid, page, pageSize, debounced, filtersParam, refreshKey, sortBy, sortDir]);
|
}, [client, rvUid, preset_alias, page, pageSize, debounced, filtersParam, refreshKey, sortBy, sortDir]);
|
||||||
|
|
||||||
const fields: RecordViewField[] = useMemo(() => {
|
const fields: RecordViewField[] = useMemo(() => {
|
||||||
const all = resp?.config.fields ?? [];
|
const all = resp?.config.fields ?? [];
|
||||||
|
|||||||
95
src/screens/AllDailyLogsPage.tsx
Normal file
95
src/screens/AllDailyLogsPage.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { Modal } from '../components/reusable';
|
||||||
|
import { DailyLogsView } from '../components/rv';
|
||||||
|
import { DailyLogDetail } from '../components/dv';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { ClipboardList } from 'lucide-react';
|
||||||
|
import { DynamicForm } from '../components/forms/DynamicForm';
|
||||||
|
import { DAILY_REPORTS } from '../api/config';
|
||||||
|
import { dailyReportsClient } from '../api/clients';
|
||||||
|
|
||||||
|
export function AllDailyLogsPage() {
|
||||||
|
const params = useParams();
|
||||||
|
const instanceId = params.instanceId ? Number(params.instanceId) : undefined;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
|
const [createTitle, setCreateTitle] = useState("Punch In");
|
||||||
|
const [punchOutInstanceId, setPunchOutInstanceId] = useState<number | string | null>(null);
|
||||||
|
const [punchOutTitle, setPunchOutTitle] = useState("Punch Out");
|
||||||
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DailyLogsView
|
||||||
|
refreshKey={refreshKey}
|
||||||
|
preset_alias={undefined}
|
||||||
|
title="All Daily Logs"
|
||||||
|
onRowClick={(row) => {
|
||||||
|
const id = row.instance_id as number | string | undefined;
|
||||||
|
if (id != null) navigate(`/all-daily/${id}`);
|
||||||
|
}}
|
||||||
|
onPunchOutRow={(row) => {
|
||||||
|
setPunchOutInstanceId(row.instance_id as string | number);
|
||||||
|
setPunchOutTitle("Punch Out");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Global Floating Action Button for Punch In */}
|
||||||
|
<button
|
||||||
|
onClick={() => { setIsCreating(true); setCreateTitle("Punch In"); }}
|
||||||
|
className="fixed bottom-24 right-6 w-[60px] h-[60px] bg-[var(--z-bg-primary-400)] rounded-3xl flex items-center justify-center shadow-[0_4px_12px_rgba(var(--z-bg-primary-400-rgb),0.4)] text-white z-40 hover:opacity-90 transition-transform hover:scale-105 active:scale-95 cursor-pointer"
|
||||||
|
>
|
||||||
|
<ClipboardList size={24} className="text-white" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={isCreating}
|
||||||
|
onClose={() => setIsCreating(false)}
|
||||||
|
title={createTitle}
|
||||||
|
width="md"
|
||||||
|
>
|
||||||
|
<DynamicForm
|
||||||
|
client={dailyReportsClient}
|
||||||
|
activityId={DAILY_REPORTS.activities.INIT.uid}
|
||||||
|
onSuccess={() => {
|
||||||
|
setIsCreating(false);
|
||||||
|
setRefreshKey(k => k + 1);
|
||||||
|
}}
|
||||||
|
onCancel={() => setIsCreating(false)}
|
||||||
|
onActivityChange={(name) => setCreateTitle(name)}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={punchOutInstanceId != null}
|
||||||
|
onClose={() => setPunchOutInstanceId(null)}
|
||||||
|
title={punchOutTitle}
|
||||||
|
width="md"
|
||||||
|
>
|
||||||
|
{punchOutInstanceId != null && (
|
||||||
|
<DynamicForm
|
||||||
|
client={dailyReportsClient}
|
||||||
|
activityId={DAILY_REPORTS.activities.PUNCH_OUT.uid}
|
||||||
|
instanceId={punchOutInstanceId}
|
||||||
|
onSuccess={() => {
|
||||||
|
setPunchOutInstanceId(null);
|
||||||
|
setRefreshKey(k => k + 1);
|
||||||
|
}}
|
||||||
|
onCancel={() => setPunchOutInstanceId(null)}
|
||||||
|
onActivityChange={(name) => setPunchOutTitle(name)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={instanceId != null}
|
||||||
|
onClose={() => navigate(`/all-daily`)}
|
||||||
|
title={instanceId != null ? `Daily Log #${instanceId}` : undefined}
|
||||||
|
width="lg"
|
||||||
|
>
|
||||||
|
{instanceId != null && <DailyLogDetail instanceId={instanceId} />}
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -52,9 +52,9 @@ export function ConsoleLayout() {
|
|||||||
|
|
||||||
<nav
|
<nav
|
||||||
className="fixed bottom-0 inset-x-0 z-20 h-16 pb-[env(safe-area-inset-bottom)] bg-card border-t border-border-subtle grid shadow-[0_-2px_16px_rgba(11,27,59,0.06)]"
|
className="fixed bottom-0 inset-x-0 z-20 h-16 pb-[env(safe-area-inset-bottom)] bg-card border-t border-border-subtle grid shadow-[0_-2px_16px_rgba(11,27,59,0.06)]"
|
||||||
style={{ gridTemplateColumns: `repeat(${SCREENS.filter(t => t.key !== 'analytics').length}, minmax(0, 1fr))` }}
|
style={{ gridTemplateColumns: `repeat(${SCREENS.filter(t => t.key !== 'analytics' && t.key !== 'all-daily').length}, minmax(0, 1fr))` }}
|
||||||
>
|
>
|
||||||
{SCREENS.filter(t => t.key !== 'analytics').map((t) => {
|
{SCREENS.filter(t => t.key !== 'analytics' && t.key !== 'all-daily').map((t) => {
|
||||||
const Icon = t.icon;
|
const Icon = t.icon;
|
||||||
return (
|
return (
|
||||||
<NavLink
|
<NavLink
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Store, Phone, ShoppingCart, ClipboardList, type LucideIcon } from 'lucide-react';
|
import { Store, Phone, ShoppingCart, ClipboardList, Home, type LucideIcon } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
OrdersView,
|
OrdersView,
|
||||||
CallsView,
|
CallsView,
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
import { AnalyticsPage } from './AnalyticsPage';
|
import { AnalyticsPage } from './AnalyticsPage';
|
||||||
import { BarChart2 } from 'lucide-react';
|
import { BarChart2 } from 'lucide-react';
|
||||||
|
|
||||||
export type ScreenKey = 'orders' | 'calls' | 'stores' | 'daily' | 'analytics';
|
export type ScreenKey = 'orders' | 'calls' | 'stores' | 'daily' | 'analytics' | 'all-daily';
|
||||||
|
|
||||||
export interface ScreenDef {
|
export interface ScreenDef {
|
||||||
key: ScreenKey;
|
key: ScreenKey;
|
||||||
@ -31,10 +31,11 @@ export interface ScreenDef {
|
|||||||
|
|
||||||
export const SCREENS: ScreenDef[] = [
|
export const SCREENS: ScreenDef[] = [
|
||||||
{ key: 'analytics', label: 'Analytics', icon: BarChart2, View: AnalyticsPage as any },
|
{ key: 'analytics', label: 'Analytics', icon: BarChart2, View: AnalyticsPage as any },
|
||||||
|
{ key: 'daily', label: 'Home', icon: Home, View: DailyLogsView, Detail: DailyLogDetail, noun: 'Daily Log' },
|
||||||
{ key: 'orders', label: 'Orders', icon: ShoppingCart, View: OrdersView, Detail: OrderDetail, noun: 'Order' },
|
{ key: 'orders', label: 'Orders', icon: ShoppingCart, View: OrdersView, Detail: OrderDetail, noun: 'Order' },
|
||||||
{ key: 'calls', label: 'Calls', icon: Phone, View: CallsView, Detail: CallDetail, noun: 'Call' },
|
{ key: 'calls', label: 'Calls', icon: Phone, View: CallsView, Detail: CallDetail, noun: 'Call' },
|
||||||
{ key: 'stores', label: 'Stores', icon: Store, View: StoresView, Detail: StoreDetail, noun: 'Store' },
|
{ key: 'stores', label: 'Stores', icon: Store, View: StoresView, Detail: StoreDetail, noun: 'Store' },
|
||||||
{ key: 'daily', label: 'Daily Logs', icon: ClipboardList, View: DailyLogsView, Detail: DailyLogDetail, noun: 'Daily Log' },
|
{ key: 'all-daily', label: 'All Logs', icon: ClipboardList, View: DailyLogsView, Detail: DailyLogDetail, noun: 'Daily Log' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function screenByKey(key: string | undefined): ScreenDef | undefined {
|
export function screenByKey(key: string | undefined): ScreenDef | undefined {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user