Load in improvements
This commit is contained in:
+10
-1
@@ -2,6 +2,7 @@ import type { Component } from 'solid-js';
|
|||||||
import { A, useLocation, useNavigate } from '@solidjs/router';
|
import { A, useLocation, useNavigate } from '@solidjs/router';
|
||||||
import { Show } from 'solid-js';
|
import { Show } from 'solid-js';
|
||||||
import { authStore } from './store/authStore';
|
import { authStore } from './store/authStore';
|
||||||
|
import { appStore } from './store/appStore';
|
||||||
|
|
||||||
interface AppContainerProps {
|
interface AppContainerProps {
|
||||||
children?: any;
|
children?: any;
|
||||||
@@ -25,7 +26,15 @@ const AppContainer: Component<AppContainerProps> = (props) => {
|
|||||||
<div class="flex justify-between h-16">
|
<div class="flex justify-between h-16">
|
||||||
<div class="flex items-center gap-8">
|
<div class="flex items-center gap-8">
|
||||||
<div class="flex-shrink-0 flex items-center">
|
<div class="flex-shrink-0 flex items-center">
|
||||||
<span class="font-bold text-xl text-gray-900 tracking-tight">EstiMaker</span>
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
appStore.resetEstimate();
|
||||||
|
navigate('/');
|
||||||
|
}}
|
||||||
|
class="font-bold text-xl text-gray-900 tracking-tight hover:text-blue-600 transition-colors"
|
||||||
|
>
|
||||||
|
EstiMaker
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<nav class="flex space-x-4">
|
<nav class="flex space-x-4">
|
||||||
<A
|
<A
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ const ItemExtractor: Component<ItemExtractorProps> = () => {
|
|||||||
<Show when={mode() === 'initial'}>
|
<Show when={mode() === 'initial'}>
|
||||||
<div class="flex flex-col items-center justify-center space-y-8 py-12">
|
<div class="flex flex-col items-center justify-center space-y-8 py-12">
|
||||||
<div class="text-center mb-4">
|
<div class="text-center mb-4">
|
||||||
<h1 class="text-4xl font-black text-gray-900 mb-3 tracking-tight lowercase">EstiMaker</h1>
|
<h1 class="text-4xl font-black text-gray-900 mb-3 tracking-tight">EstiMaker</h1>
|
||||||
<p class="text-gray-500 text-lg">Start a new project or continue where you left off.</p>
|
<p class="text-gray-500 text-lg">Start a new project or continue where you left off.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Component } from 'solid-js';
|
import type { Component } from 'solid-js';
|
||||||
import { createSignal, Show, For, createEffect } from 'solid-js';
|
import { createSignal, Show, For, createEffect } from 'solid-js';
|
||||||
import { Portal } from 'solid-js/web';
|
import { Portal } from 'solid-js/web';
|
||||||
import { X, User } from 'lucide-solid';
|
import { X } from 'lucide-solid';
|
||||||
import { pb, COLLECTIONS } from '../utils/db';
|
import { pb, COLLECTIONS } from '../utils/db';
|
||||||
import { appStore } from '../store/appStore';
|
import { appStore } from '../store/appStore';
|
||||||
import { normalizeScopes } from '../utils/scope-utils';
|
import { normalizeScopes } from '../utils/scope-utils';
|
||||||
@@ -14,6 +14,7 @@ interface CloudLoadModalProps {
|
|||||||
const CloudLoadModal: Component<CloudLoadModalProps> = (props) => {
|
const CloudLoadModal: Component<CloudLoadModalProps> = (props) => {
|
||||||
const [cloudEstimates, setCloudEstimates] = createSignal<any[]>([]);
|
const [cloudEstimates, setCloudEstimates] = createSignal<any[]>([]);
|
||||||
const [isCloudLoading, setIsCloudLoading] = createSignal(false);
|
const [isCloudLoading, setIsCloudLoading] = createSignal(false);
|
||||||
|
const [loadingRecordId, setLoadingRecordId] = createSignal<string | null>(null);
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (props.show) {
|
if (props.show) {
|
||||||
@@ -24,8 +25,10 @@ const CloudLoadModal: Component<CloudLoadModalProps> = (props) => {
|
|||||||
const loadCloudEstimatesList = async () => {
|
const loadCloudEstimatesList = async () => {
|
||||||
setIsCloudLoading(true);
|
setIsCloudLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// Optimized: Only fetch metadata fields to keep the list response small and fast
|
||||||
const records = await pb.collection(COLLECTIONS.ESTIMATES).getFullList({
|
const records = await pb.collection(COLLECTIONS.ESTIMATES).getFullList({
|
||||||
sort: '-created'
|
sort: '-created',
|
||||||
|
fields: 'id,name,created,updated'
|
||||||
});
|
});
|
||||||
setCloudEstimates(records);
|
setCloudEstimates(records);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -36,28 +39,36 @@ const CloudLoadModal: Component<CloudLoadModalProps> = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadFromCloud = (record: any) => {
|
const loadFromCloud = async (recordMetadata: any) => {
|
||||||
const data = record.items; // items holds the full JSON blob
|
setLoadingRecordId(recordMetadata.id);
|
||||||
if (!data) return;
|
try {
|
||||||
|
// Fetch the full record with all fields (items, history) only when requested
|
||||||
|
const record = await pb.collection(COLLECTIONS.ESTIMATES).getOne(recordMetadata.id);
|
||||||
|
const data = record.items;
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
if (data.scopes) appStore.setScopes(normalizeScopes(data.scopes));
|
if (data.scopes) appStore.setScopes(normalizeScopes(data.scopes));
|
||||||
if (data.subScopes) appStore.setSubScopes(data.subScopes);
|
if (data.subScopes) appStore.setSubScopes(data.subScopes);
|
||||||
if (data.items) appStore.setEstimateItems(data.items);
|
if (data.items) appStore.setEstimateItems(data.items);
|
||||||
if (data.clientInfo) appStore.setClientInfo(data.clientInfo);
|
if (data.clientInfo) appStore.setClientInfo(data.clientInfo);
|
||||||
if (data.jobInfo) appStore.setJobInfo(data.jobInfo);
|
if (data.jobInfo) appStore.setJobInfo(data.jobInfo);
|
||||||
if (data.generalEstimateNotes !== undefined) appStore.setGeneralEstimateNotes(data.generalEstimateNotes);
|
if (data.generalEstimateNotes !== undefined) appStore.setGeneralEstimateNotes(data.generalEstimateNotes);
|
||||||
if (data.generalPreNotes !== undefined) appStore.setGeneralPreNotes(data.generalPreNotes);
|
if (data.generalPreNotes !== undefined) appStore.setGeneralPreNotes(data.generalPreNotes);
|
||||||
|
|
||||||
appStore.setEstimateName(record.name);
|
appStore.setEstimateName(record.name);
|
||||||
appStore.setEstimateId(record.id);
|
appStore.setEstimateId(record.id);
|
||||||
appStore.setHistory(record.history || []);
|
appStore.setHistory(record.history || []);
|
||||||
appStore.setLatestSnapshot(record.items);
|
appStore.setLatestSnapshot(record.items);
|
||||||
appStore.setIsLatestVersion(true);
|
appStore.setIsLatestVersion(true);
|
||||||
|
|
||||||
// Also clear out the extracted CSV items so we don't accidentally switch back to the upload screen or cause conflicts
|
appStore.setItems([]);
|
||||||
appStore.setItems([]);
|
props.onClose();
|
||||||
|
} catch (err) {
|
||||||
props.onClose();
|
console.error(err);
|
||||||
|
alert('Failed to load full estimate data.');
|
||||||
|
} finally {
|
||||||
|
setLoadingRecordId(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -85,18 +96,13 @@ const CloudLoadModal: Component<CloudLoadModalProps> = (props) => {
|
|||||||
<p class="text-[10px] text-muted-foreground font-medium uppercase tracking-wider">
|
<p class="text-[10px] text-muted-foreground font-medium uppercase tracking-wider">
|
||||||
{new Date(record.created).toLocaleDateString()}
|
{new Date(record.created).toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
<Show when={record.items?.savedBy}>
|
|
||||||
<div class="flex items-center gap-1.5 mt-1.5 grayscale opacity-70">
|
|
||||||
<User class="w-3 h-3 text-muted-foreground" />
|
|
||||||
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-tight">{record.items.savedBy}</span>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
disabled={loadingRecordId() !== null}
|
||||||
onClick={() => loadFromCloud(record)}
|
onClick={() => loadFromCloud(record)}
|
||||||
class="px-4 py-2 bg-background border border-border rounded-xl text-xs font-bold shadow-sm hover:text-primary hover:border-primary/50 transition-all opacity-0 group-hover:opacity-100"
|
class="px-4 py-2 bg-background border border-border rounded-xl text-xs font-bold shadow-sm hover:text-primary hover:border-primary/50 transition-all opacity-0 group-hover:opacity-100 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Load
|
{loadingRecordId() === record.id ? 'Fetching...' : 'Load'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user