From 786a90dae971f1fe17bc3df16a60ab612141caa1 Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Thu, 21 Aug 2025 02:16:02 +0530 Subject: [PATCH 1/9] changes --- ONBOARDING_TEST.md | 57 ++++++++ package-lock.json | 4 +- src/App.tsx | 8 +- src/components/FileMenu/FileOptions.tsx | 101 ++------------ src/pages/Home.tsx | 169 +++++++++++++++++++++--- src/pages/LandingPage.tsx | 4 + src/pages/SettingsPage.tsx | 24 ++++ src/utils/helper.ts | 33 +++++ vercel.json | 22 +++ 9 files changed, 309 insertions(+), 113 deletions(-) create mode 100644 ONBOARDING_TEST.md create mode 100644 vercel.json diff --git a/ONBOARDING_TEST.md b/ONBOARDING_TEST.md new file mode 100644 index 0000000..18d0229 --- /dev/null +++ b/ONBOARDING_TEST.md @@ -0,0 +1,57 @@ +# User Onboarding Flow Test + +This document explains how to test the new user onboarding functionality. + +## How It Works + +### First Time User (New User) + +1. Visit the app for the first time +2. The landing page will be shown automatically +3. Click "Start Creating Invoices" or "Access Invoice Editor" button +4. User is redirected to `/app/editor` +5. The `isNewUser` flag is set to `false` in localStorage + +### Returning User (Existing User) + +1. Visit the app after completing onboarding +2. User is automatically redirected to `/app/editor` +3. Landing page is skipped + +### Testing the Reset Functionality + +1. Go to Settings page (`/app/settings`) +2. In the "Preferences" section, click "Reset Onboarding" +3. A success toast will appear: "Onboarding reset! Landing page will show on next visit." +4. Navigate to the home page (`/`) or refresh +5. Landing page will be shown again + +## Technical Implementation + +### Files Modified: + +1. `src/utils/helper.ts` - Added localStorage utility functions +2. `src/App.tsx` - Added conditional rendering logic +3. `src/pages/LandingPage.tsx` - Updated button handler to mark user as existing +4. `src/pages/SettingsPage.tsx` - Added reset onboarding option + +### LocalStorage Key: + +- Key: `invoiceApp_isNewUser` +- Values: + - `null` or `"true"` = New user (show landing page) + - `"false"` = Existing user (skip to editor) + +### API Functions: + +- `isNewUser()` - Returns boolean indicating if user is new +- `markUserAsExisting()` - Sets user as existing (called on button click) +- `resetUserOnboarding()` - Resets user to new status (for testing/reset) + +## User Flow: + +``` +First Visit -> Landing Page -> Click Button -> Set isNewUser=false -> Redirect to /app/editor +Next Visit -> Check isNewUser -> false -> Direct to /app/editor (skip landing) +Reset Option -> Click "Reset Onboarding" -> Set isNewUser=true -> Next visit shows landing +``` diff --git a/package-lock.json b/package-lock.json index c3d56bc..773201d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "Govt Invoice", - "version": "0.0.1", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Govt Invoice", - "version": "0.0.1", + "version": "0.0.3", "dependencies": { "@bcyesil/capacitor-plugin-printer": "^0.0.5", "@capacitor/android": "^6.0.0", diff --git a/src/App.tsx b/src/App.tsx index 1c2d0a5..226ea94 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,6 +20,7 @@ import { InvoiceProvider } from "./contexts/InvoiceContext"; import PWAUpdatePrompt from "./components/PWAUpdatePrompt"; import OfflineIndicator from "./components/OfflineIndicator"; import { usePWA } from "./hooks/usePWA"; +import { isNewUser } from "./utils/helper"; /* Core CSS required for Ionic components to work properly */ import "@ionic/react/css/core.css"; @@ -46,6 +47,7 @@ setupIonicReact(); const AppContent: React.FC = () => { const { isDarkMode } = useTheme(); const { isOnline } = usePWA(); + const showLandingPage = isNewUser(); return ( @@ -53,7 +55,11 @@ const AppContent: React.FC = () => { - + {showLandingPage ? ( + + ) : ( + + )} diff --git a/src/components/FileMenu/FileOptions.tsx b/src/components/FileMenu/FileOptions.tsx index dedc15b..9c6f907 100644 --- a/src/components/FileMenu/FileOptions.tsx +++ b/src/components/FileMenu/FileOptions.tsx @@ -43,6 +43,7 @@ import { checkmark, logoBuffer, pencilOutline, + colorPaletteOutline, } from "ionicons/icons"; import * as AppGeneral from "../socialcalc/index.js"; import { File } from "../Storage/LocalStorage.js"; @@ -59,11 +60,15 @@ import { interface FileOptionsProps { showActionsPopover: boolean; setShowActionsPopover: (show: boolean) => void; + showColorModal: boolean; + setShowColorPicker: (show: boolean) => void; } const FileOptions: React.FC = ({ showActionsPopover, setShowActionsPopover, + showColorModal, + setShowColorPicker, }) => { const { isDarkMode } = useTheme(); const [showToast, setShowToast] = useState(false); @@ -151,12 +156,10 @@ const FileOptions: React.FC = ({ const handleUndo = () => { AppGeneral.undo(); - setShowActionsPopover(false); }; const handleRedo = () => { AppGeneral.redo(); - setShowActionsPopover(false); }; const _validateName = (filename: string) => { @@ -355,81 +358,12 @@ const FileOptions: React.FC = ({ } }; - const handleAddImage = () => { - setShowActionsPopover(false); - fileInputRef.current?.click(); - }; - - const handleImageUpload = (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - const imageUrl = e.target?.result as string; - if (imageUrl) { - // Get current selected cell - const currentCell = getCurrentSelectedCell(); - if (currentCell) { - AppGeneral.addLogo(currentCell, imageUrl); - setToastMessage("Image added successfully!"); - setShowToast(true); - } else { - setToastMessage("Please select a cell first"); - setShowToast(true); - } - } - }; - reader.readAsDataURL(file); - } - }; - const getCurrentSelectedCell = (): string | null => { // This would typically get the currently selected cell from the spreadsheet // For now, return a default cell return "A1"; }; - const handleTakePhoto = async () => { - setShowActionsPopover(false); - try { - const image = await Camera.getPhoto({ - quality: 90, - allowEditing: true, - resultType: CameraResultType.DataUrl, - source: CameraSource.Camera, - }); - - if (image.dataUrl) { - const currentCell = getCurrentSelectedCell(); - if (currentCell) { - AppGeneral.addLogo(currentCell, image.dataUrl); - setToastMessage("Photo added successfully!"); - setShowToast(true); - } else { - setToastMessage("Please select a cell first"); - setShowToast(true); - } - } - } catch (error) { - console.error("Error taking photo:", error); - setToastMessage("Failed to take photo"); - setShowToast(true); - } - }; - - const handleRemoveImage = () => { - setShowActionsPopover(false); - const currentCell = getCurrentSelectedCell(); - if (currentCell) { - AppGeneral.removeLogo(currentCell); - setToastMessage("Image removed successfully!"); - setShowToast(true); - } else { - setToastMessage("Please select a cell with an image first"); - setShowToast(true); - } - }; - const handleRemoveLogo = () => { setShowActionsPopover(false); const logoCoordinates = AppGeneral.getLogoCoordinates(); @@ -504,14 +438,9 @@ const FileOptions: React.FC = ({ Redo - - - Add Image - - - - - Take Photo + setShowColorPicker(true)}> + + Sheet Colors @@ -533,24 +462,10 @@ const FileOptions: React.FC = ({ Remove Signature - - - - Remove Image - - {/* Hidden file input for image upload */} - - {/* Unsaved Changes Confirmation Alert */} { // Invoice form state const [showInvoiceForm, setShowInvoiceForm] = useState(false); + // Error state for initialization failures + const [initError, setInitError] = useState(false); + const [fileIsEmpty, setFileIsEmpty] = useState(false); + // Available colors for sheet themes const availableColors = [ { name: "red", label: "Red", color: "#ff4444" }, @@ -184,6 +188,44 @@ const Home: React.FC = () => { setShowColorModal(true); }; + const handleRefreshFile = async () => { + try { + setInitError(false); + setFileIsEmpty(false); + + // Clear the existing content + const container = document.getElementById("container"); + if (container) { + const workbookControl = document.getElementById("workbookControl"); + const tableeditor = document.getElementById("tableeditor"); + const msg = document.getElementById("msg"); + + if (workbookControl) workbookControl.innerHTML = ""; + if (tableeditor) tableeditor.innerHTML = ""; + if (msg) msg.innerHTML = ""; + } + + // Re-initialize with template data + const data = DATA["home"]["App"]["msc"]; + AppGeneral.initializeApp(JSON.stringify(data)); + + // Save the refreshed template as the default file + const initialContent = encodeURIComponent(JSON.stringify(data)); + const now = new Date().toISOString(); + const file = new File(now, now, initialContent, "default", billType); + await store._saveFile(file); + + setToastMessage("File refreshed successfully!"); + setToastColor("success"); + setShowToast(true); + } catch (error) { + console.error("Error refreshing file:", error); + setToastMessage("Failed to refresh file. Please try again."); + setToastColor("danger"); + setShowToast(true); + } + }; + const executeSaveAsWithFilename = async (filename: string) => { updateSelectedFile(filename); @@ -221,14 +263,26 @@ const Home: React.FC = () => { useEffect(() => { const initializeApp = async () => { try { + setInitError(false); + setFileIsEmpty(false); + // First try to load the default file from local storage const defaultExists = await store._checkKey("default"); if (defaultExists) { const defaultFile = await store._getFile("default"); const decodedContent = decodeURIComponent(defaultFile.content); - AppGeneral.viewFile("default", decodedContent); - updateBillType(defaultFile.billType); - console.log("Loaded existing default file from local storage"); + + // Check if the file is empty using the helper function + const isEmpty = isDefaultFileEmpty(decodedContent); + + if (isEmpty) { + setFileIsEmpty(true); + console.log("Default file is empty, showing refresh option"); + } else { + AppGeneral.viewFile("default", decodedContent); + updateBillType(defaultFile.billType); + console.log("Loaded existing default file from local storage"); + } } else { // If no default file exists, initialize with template data and save it const data = DATA["home"]["App"]["msc"]; @@ -244,6 +298,36 @@ const Home: React.FC = () => { } catch (error) { console.error("Error initializing app:", error); + // Check if this is the specific workbook error and if file is empty + const isWorkbookError = + error.message && + error.message.includes( + "Cannot read properties of null (reading 'workbook')" + ); + + if (isWorkbookError) { + try { + // Check if the current file is empty + const defaultExists = await store._checkKey("default"); + if (defaultExists) { + const defaultFile = await store._getFile("default"); + const decodedContent = decodeURIComponent(defaultFile.content); + const isEmpty = isDefaultFileEmpty(decodedContent); + + if (isEmpty) { + setInitError(true); + setFileIsEmpty(true); + console.log( + "Workbook error with empty file, showing refresh button" + ); + return; // Don't proceed with fallback initialization + } + } + } catch (checkError) { + console.error("Error checking file emptiness:", checkError); + } + } + // Check if the error is due to storage quota exceeded if (isQuotaExceededError(error)) { setToastMessage(getQuotaExceededMessage("initializing the app")); @@ -251,10 +335,14 @@ const Home: React.FC = () => { setShowToast(true); } - // Fallback to template initialization - const data = DATA["home"]["App"]["msc"]; - AppGeneral.initializeApp(JSON.stringify(data)); - AppGeneral.changeSheetColor("#000000"); + // Fallback to template initialization for non-workbook errors + if (!isWorkbookError) { + const data = DATA["home"]["App"]["msc"]; + AppGeneral.initializeApp(JSON.stringify(data)); + AppGeneral.changeSheetColor("#000000"); + } else { + setInitError(true); + } } // Alternative smooth scrolling implementation setTimeout(() => { @@ -522,12 +610,6 @@ const Home: React.FC = () => { style={{ cursor: "pointer", marginRight: "12px" }} title="Format Current Cell" /> - openColorModal("background")} - style={{ cursor: "pointer", marginRight: "12px" }} - /> {
-
-
-
+ {initError && fileIsEmpty ? ( +
+ +

+ Spreadsheet Failed to Load +

+

+ The file appears to be empty or corrupted. Click refresh to + reload with a fresh template. +

+ + + Refresh File + +
+ ) : ( + <> +
+
+
+ + )}
{/* Toast for save notifications */} @@ -635,6 +768,8 @@ const Home: React.FC = () => { {/* Color Picker Modal */} diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx index 98586cd..4d0e48c 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -29,6 +29,7 @@ import { } from "ionicons/icons"; import { useHistory } from "react-router-dom"; import { useTheme } from "../contexts/ThemeContext"; +import { markUserAsExisting } from "../utils/helper"; // import { cloudService } from "../services/cloud-service"; import "./LandingPage.css"; @@ -44,6 +45,9 @@ const LandingPage: React.FC = () => { }, [history]); const handleGetStarted = () => { + // Mark user as existing (no longer new) + markUserAsExisting(); + // Navigate to the editor history.push("/app/editor"); }; diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 64961ae..3362317 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -65,6 +65,7 @@ import PWAInstallPrompt from "../components/PWAInstallPrompt"; import PWADemo from "../components/PWADemo"; // import { usePushNotifications } from "../utils/pushNotifications"; import { usePWA } from "../hooks/usePWA"; +import { resetUserOnboarding } from "../utils/helper"; import "./SettingsPage.css"; // import { // cloudService, @@ -81,6 +82,7 @@ const SettingsPage: React.FC = () => { const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); const [isLoading, setIsLoading] = useState(false); + const [showResetToast, setShowResetToast] = useState(false); // PWA features // Push notifications disabled in local-only mode @@ -839,6 +841,11 @@ const SettingsPage: React.FC = () => { } }; + const handleResetOnboarding = () => { + resetUserOnboarding(); + setShowResetToast(true); + }; + React.useEffect(() => { // Push notifications disabled in local-only mode // getPermissionState().then((state) => { @@ -1379,6 +1386,13 @@ const SettingsPage: React.FC = () => { slot="end" />
+ + + +

Reset Onboarding

+

Show landing page on next visit

+
+
@@ -2194,6 +2208,16 @@ const SettingsPage: React.FC = () => { : "danger" } /> + + {/* Toast for reset onboarding confirmation */} + setShowResetToast(false)} + message="Onboarding reset! Landing page will show on next visit." + duration={3000} + position="bottom" + color="success" + /> ); }; diff --git a/src/utils/helper.ts b/src/utils/helper.ts index cf95c67..ed3eddc 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -205,3 +205,36 @@ export const getStorageManagementSuggestions = ( return suggestions; }; + +// User onboarding utilities +const USER_ONBOARDING_KEY = "invoiceApp_isNewUser"; + +export const isNewUser = (): boolean => { + try { + const stored = localStorage.getItem(USER_ONBOARDING_KEY); + // If no value is stored, user is new + if (stored === null) { + return true; + } + return stored === "true"; + } catch (error) { + console.warn("Error reading from localStorage:", error); + return true; // Default to new user if localStorage fails + } +}; + +export const markUserAsExisting = (): void => { + try { + localStorage.setItem(USER_ONBOARDING_KEY, "false"); + } catch (error) { + console.warn("Error writing to localStorage:", error); + } +}; + +export const resetUserOnboarding = (): void => { + try { + localStorage.removeItem(USER_ONBOARDING_KEY); + } catch (error) { + console.warn("Error removing from localStorage:", error); + } +}; diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..851a7ee --- /dev/null +++ b/vercel.json @@ -0,0 +1,22 @@ +{ + "headers": [ + { + "source": "/sw.js", + "headers": [ + { + "key": "Cache-Control", + "value": "no-cache, no-store, must-revalidate" + } + ] + }, + { + "source": "/manifest.json", + "headers": [ + { + "key": "Cache-Control", + "value": "public, max-age=31536000, immutable" + } + ] + } + ] +} From 99cdc92d53dbcb7af4f0a8f75fb8d1a3606ccc8c Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Thu, 21 Aug 2025 02:51:34 +0530 Subject: [PATCH 2/9] changes --- src/App.tsx | 4 +- src/app-data.ts | 16 +- src/components/Forms/android-inv-Type1.tsx | 0 src/components/OfflineIndicator.tsx | 77 +++-- src/pages/Home.tsx | 185 +---------- src/pages/SettingsPage.tsx | 338 ++++++++------------- 6 files changed, 197 insertions(+), 423 deletions(-) create mode 100644 src/components/Forms/android-inv-Type1.tsx diff --git a/src/App.tsx b/src/App.tsx index 226ea94..4753a60 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -63,17 +63,15 @@ const AppContent: React.FC = () => {
+ {!isOnline && } - {!isOnline && } - {!isOnline && } - {!isOnline && } diff --git a/src/app-data.ts b/src/app-data.ts index cc40ea1..21c3659 100644 --- a/src/app-data.ts +++ b/src/app-data.ts @@ -11,7 +11,7 @@ export let DATA = { sheet1: { sheetstr: { savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:10\ncell:C1:b:::2::l:1:f:10\ncell:D1:b:::2::l:1:f:10\ncell:E1:b:::2::l:1:f:10\ncell:F1:b:::2::l:1:f:10\ncell:G1:b:::2::l:1:f:10\ncell:A2:b::2:::l:1:f:10\ncell:B2:t:INVOICE:b:1:1:1:1:f:13:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:10\ncell:D2:t::b:2::2::l:1:f:10\ncell:E2:t::b:2::2::l:1:f:10\ncell:F2:t::b:2::2::l:1:f:10\ncell:G2:t::b:2::2::l:1:f:10\ncell:A3:b::2:::l:3:f:10\ncell:B3:b:2:::2:l:3:f:10\ncell:C3:b:2::::l:1:f:10\ncell:D3:b:2::::l:1:f:10\ncell:E3:b:2::::l:1:f:10\ncell:F3:b:2::::l:1:f:10\ncell:G3:b:2:2:::l:3:f:10\ncell:A4:b::2:::l:3:f:10\ncell:B4:b::::2:l:3:f:6\ncell:C4:t:BILL TO\\c:f:9:colspan:2\ncell:E4:f:5:cf:2\ncell:G4:b::2:::l:3:f:10\ncell:A5:b::2:::l:3:f:10\ncell:B5:b::::2:l:3:f:7\ncell:C5:t:[Name]:f:9:cf:2:colspan:3\ncell:E5:f:1:cf:2\ncell:F5:t::l:2:f:10:tvf:4:rowspan:4\ncell:G5:t::b::1:::l:2:f:10\ncell:A6:b::2:::l:3:f:10\ncell:B6:b::::2:l:3:f:7\ncell:C6:t:[Street Address]:f:9:cf:2:colspan:3\ncell:E6:f:1:cf:2\ncell:F6:l:2:f:10\ncell:G6:b::1:::l:2:f:10\ncell:A7:b::2:::l:3:f:10\ncell:B7:b::::2:l:3:f:7\ncell:C7:t:[City, State, Zip]:f:9:cf:2:colspan:3\ncell:E7:f:1:cf:2\ncell:G7:b::2:::l:3:f:10\ncell:A8:b::2:::l:3:f:10\ncell:B8:b::::2:l:3:f:7\ncell:C8:t:Phone\\c:f:9:cf:2:colspan:3\ncell:D8:f:8:cf:2\ncell:E8:f:1:cf:2\ncell:G8:b::2:::l:3:f:10\ncell:A9:b::2:::l:3:f:10\ncell:B9:b::::2:l:3:f:7\ncell:C9:t:Email\\c:f:9:cf:2:colspan:3\ncell:E9:f:1:cf:2\ncell:G9:b::2:::l:3:f:10\ncell:A10:b::2:::l:3:f:10\ncell:B10:b::::2:l:3:f:7\ncell:C10:colspan:2\ncell:G10:b::2:::l:3:f:10\ncell:A11:b::2:::l:3:f:10\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:FROM\\c:f:9:colspan:2\ncell:E11:f:8:colspan:2\ncell:G11:b::2:::l:3:f:10\ncell:A12:b::2:::l:3:f:10\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[Name]:f:9:colspan:4\ncell:E12:colspan:2\ncell:G12:b::2:::l:3:f:10\ncell:A13:b::2:::l:3:f:10\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:[Street Address]:f:9:colspan:4\ncell:E13:colspan:2\ncell:G13:b::2:::l:3:f:10\ncell:A14:b::2:::l:3:f:10\ncell:B14:b::::2:l:3:f:7\ncell:C14:t:[City, State, Zip]:f:9:colspan:4\ncell:E14:colspan:2\ncell:G14:b::2:::l:3:f:10\ncell:A15:b::2:::l:3:f:10\ncell:B15:b::::2:l:3:f:7\ncell:C15:t:Phone\\c:f:9:colspan:4\ncell:D15:f:8:cf:2:colspan:3\ncell:E15:colspan:2\ncell:G15:b::2:::l:3:f:10\ncell:A16:b::2:::l:3:f:10\ncell:B16:b::::2:l:3:f:7\ncell:C16:t:Email\\c:f:9:colspan:4\ncell:E16:colspan:2\ncell:G16:b::2:::l:3:f:10\ncell:A17:b::2:::l:3:f:10\ncell:B17:b::::2:l:3:f:6\ncell:G17:b::2:::l:3:f:10\ncell:A18:b::2:::l:3:f:10\ncell:B18:b::::2:l:3:f:10\ncell:C18:t:INVOICE #\\c:f:9:cf:2:colspan:4\ncell:D18:v:1:l:4:f:9:cf:2:colspan:3\ncell:G18:b::2:::l:3:f:10\ncell:A19:b::2:::l:3:f:10\ncell:B19:b::::2:l:3:f:10\ncell:G19:b::2:::l:3:f:10\ncell:A20:b::2:::l:3:f:10\ncell:B20:b::::2:l:3:f:10\ncell:C20:t:DATE\\c:f:9:cf:2\ncell:D20:vtf:nd:45860:TODAY():f:9:cf:2:ntvf:3:colspan:2\ncell:G20:b::2:::l:3:f:10\ncell:A21:b::2:::l:3:f:10\ncell:B21:b::::2:l:3:f:6:cf:2\ncell:C21:b:::2::l:1:f:10\ncell:D21:b:::2::l:3:f:6:cf:2\ncell:E21:b:::2::l:1:f:10\ncell:F21:b:::2::l:1:f:10\ncell:G21:b::2:::l:3:f:10\ncell:A22:b::2:::l:3:f:10\ncell:B22:b::2::2:l:3:f:10\ncell:C22:t:Description:b:1::1:1:f:9:cf:1:colspan:3\ncell:D22:t:Description:b:1::1::l:3:f:6:cf:2:colspan:2:rowspan:1\ncell:E22:t::b:2:2:2::l:1:f:10\ncell:F22:t:Amount:b:1:1:1:1:f:9:cf:1\ncell:G22:b::2::2:l:3:f:10\ncell:A23:b::2:::l:3:f:10\ncell:B23:b::2::2:l:3:f:10\ncell:C23:b:1:1::1:f:2:cf:2:colspan:3\ncell:D23:b:1:1:::l:3:f:10:cf:2:colspan:2:rowspan:1\ncell:E23:t::b:2:2:2::l:1:f:10\ncell:F23:b:1:1:::f:2:ntvf:1\ncell:G23:b::2::2:l:3:f:10\ncell:A24:b::2:::l:3:f:10\ncell:B24:b::2::2:l:3:f:10:cf:2\ncell:C24:b::1::1:f:2:cf:2:colspan:3\ncell:D24:b::1:::l:1:f:10:colspan:2\ncell:E24:l:1:f:10\ncell:F24:b::1:::f:2:ntvf:1\ncell:G24:b::2::2:l:3:f:10\ncell:A25:b::2:::l:3:f:10\ncell:B25:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C25:b::1::1:f:2:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:2:ntvf:1\ncell:G25:b::2::2:l:3:f:10\ncell:A26:b::2:::l:3:f:10\ncell:B26:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C26:b::1::1:f:2:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:2:ntvf:1\ncell:G26:b::2::2:l:3:f:10\ncell:A27:b::2:::l:3:f:10\ncell:B27:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C27:b::1::1:f:2:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:2:ntvf:1\ncell:G27:b::2::2:l:3:f:10\ncell:A28:b::2:::l:3:f:10\ncell:B28:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C28:b::1::1:f:2:cf:2:colspan:3\ncell:D28:b::1:::colspan:2\ncell:F28:b::1:::f:2:ntvf:1\ncell:G28:b::2::2:l:3:f:10\ncell:A29:b::2:::l:3:f:10\ncell:B29:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C29:b::1::1:f:2:cf:2:colspan:3\ncell:D29:b::1:::colspan:2\ncell:F29:b::1:::f:2:ntvf:1\ncell:G29:b::2::2:l:3:f:10\ncell:A30:b::2:::l:3:f:10\ncell:B30:b::2::2:l:3:f:10:cf:2\ncell:C30:b::1::1:f:2:cf:2:colspan:3\ncell:D30:b::1:::colspan:2\ncell:F30:b::1:::f:2:ntvf:1\ncell:G30:b::2::2:l:3:f:10\ncell:A31:b::2:::l:3:f:10\ncell:B31:b::2::2:l:3:f:10:cf:2\ncell:C31:b::1::1:f:2:cf:2:colspan:3\ncell:D31:b::1:::colspan:2\ncell:F31:b::1:::f:2:ntvf:1\ncell:G31:b::2::2:l:3:f:10\ncell:A32:b::2:::l:3:f:10\ncell:B32:b::2::2:l:3:f:10:cf:2\ncell:C32:b::1::1:f:2:cf:2:colspan:3\ncell:D32:b::1:::colspan:2\ncell:F32:b::1:::f:2:ntvf:1\ncell:G32:b::2::2:l:3:f:10\ncell:A33:b::2:::l:3:f:10\ncell:B33:b::2::2:l:3:f:10:cf:2\ncell:C33:b::1::1:f:2:cf:2:colspan:3\ncell:D33:b::1:::colspan:2\ncell:F33:b::1:::f:2:ntvf:1\ncell:G33:b::2::2:l:3:f:10\ncell:A34:b::2:::l:3:f:10\ncell:B34:b::2::2:l:3:f:10:cf:2\ncell:C34:b::1::1:f:2:cf:2:colspan:3\ncell:D34:b::1:::colspan:2\ncell:F34:b::1:::f:2:ntvf:1\ncell:G34:b::2::2:l:3:f:10\ncell:A35:b::2:::l:3:f:7\ncell:B35:b::2::2:l:3:f:10:cf:2\ncell:C35:b::1:1:1:f:2:cf:2:colspan:3\ncell:D35:b::1:1::l:3:f:10:cf:2:colspan:2\ncell:E35:b:::2::l:3:f:10:cf:2\ncell:F35:b::1:1::f:2:ntvf:1\ncell:G35:b::2::2:l:3:f:10\ncell:A36:b::2:::l:3:f:6\ncell:B36:b::2::2:l:3:f:6\ncell:C36:t:TOTAL:b:1:1:1:1:f:11:cf:2:colspan:3\ncell:D36:b:2::2::l:3:f:12:cf:2\ncell:E36:b:2:2:2::l:3:f:12:cf:2\ncell:F36:vtf:n:0:SUM(F23\\cF35):b:1:1:1::f:11:ntvf:1\ncell:G36:b::2::2:l:3:f:7\ncell:A37:b::2:::l:3:f:10\ncell:B37:b::::2:l:3:f:6:cf:2\ncell:C37:b:2::::l:1:f:10\ncell:D37:b:2::::l:1:f:10\ncell:E37:b:2::::l:1:f:10\ncell:F37:b:2::::l:1:f:10\ncell:G37:b::2:::l:3:f:6\ncell:A38:b::2:::l:3:f:10\ncell:B38:b::::2:l:3:f:10\ncell:D38:colspan:3:rowspan:3\ncell:G38:b::2:::l:3:f:10\ncell:A39:b::2:::l:3:f:10\ncell:B39:b::::2:l:3:f:10:cf:2\ncell:C39:f:4:cf:1\ncell:E39:f:3:colspan:2\ncell:F39:t::l:2:f:10\ncell:G39:b::2:::l:3:f:10\ncell:A40:b::2:::l:3:f:10\ncell:B40:b::::2:l:3:f:10\ncell:G40:b::2:::l:3:f:10\ncell:A41:b::2:::l:3:f:10\ncell:B41:b:::2:2:l:3:f:10\ncell:C41:b:::2::l:3:f:10\ncell:D41:b:::2::l:3:f:10\ncell:E41:b:::2::l:3:f:10\ncell:F41:b:::2::l:3:f:10\ncell:G41:b::2:2::l:3:f:10\ncell:B42:b:1:::\ncell:C42:b:1:::\ncell:D42:b:1:::\ncell:E42:b:1:::\ncell:F42:b:1:::\ncell:G42:b:1:::\ncol:A:w:10\ncol:B:w:10\ncol:C:w:62\ncol:D:w:73\ncol:E:w:15\ncol:F:w:68\ncol:G:w:10\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:15.75\nrow:36:h:15.75\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:41:h:14.25\nsheet:c:7:r:42:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:* 9pt Trebuchet MS\nfont:3:italic bold * Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Arial\nfont:7:normal bold 12pt Arial\nfont:8:normal normal * *\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 11pt Trebuchet MS\nfont:12:normal normal 12pt Arial\nfont:13:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nlayout:4:padding:* * * 4px;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + "version:1.5\ncell:B1:b:::2::l:1:f:10\ncell:C1:b:::2::l:1:f:10\ncell:D1:b:::2::l:1:f:10\ncell:E1:b:::2::l:1:f:10\ncell:F1:b:::2::l:1:f:10\ncell:G1:b:::2::l:1:f:10\ncell:A2:b::2:::l:1:f:10\ncell:B2:t:INVOICE:b:1:1:1:1:f:13:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:10\ncell:D2:t::b:2::2::l:1:f:10\ncell:E2:t::b:2::2::l:1:f:10\ncell:F2:t::b:2::2::l:1:f:10\ncell:G2:t::b:2::2::l:1:f:10\ncell:A3:b::2:::l:3:f:10\ncell:B3:b:2:::2:l:3:f:10\ncell:C3:b:2::::l:1:f:10\ncell:D3:b:2::::l:1:f:10\ncell:E3:b:2::::l:1:f:10\ncell:F3:b:2::::l:1:f:10\ncell:G3:b:2:2:::l:3:f:10\ncell:A4:b::2:::l:3:f:10\ncell:B4:b::::2:l:3:f:6\ncell:C4:t:BILL TO\\c:f:9:colspan:2\ncell:E4:f:5:cf:2\ncell:G4:b::2:::l:3:f:10\ncell:A5:b::2:::l:3:f:10\ncell:B5:b::::2:l:3:f:7\ncell:C5:t:[Name]:f:9:cf:2:colspan:3\ncell:E5:f:1:cf:2\ncell:F5:t::l:2:f:10:tvf:4:rowspan:6\ncell:G5:t::b::1:::l:2:f:10\ncell:A6:b::2:::l:3:f:10\ncell:B6:b::::2:l:3:f:7\ncell:C6:t:[Street Address]:f:9:cf:2:colspan:3\ncell:E6:f:1:cf:2\ncell:F6:l:2:f:10\ncell:G6:b::1:::l:2:f:10\ncell:A7:b::2:::l:3:f:10\ncell:B7:b::::2:l:3:f:7\ncell:C7:t:[City, State, Zip]:f:9:cf:2:colspan:3\ncell:E7:f:1:cf:2\ncell:G7:b::2:::l:3:f:10\ncell:A8:b::2:::l:3:f:10\ncell:B8:b::::2:l:3:f:7\ncell:C8:t:Phone\\c:f:9:cf:2:colspan:3\ncell:D8:f:8:cf:2\ncell:E8:f:1:cf:2\ncell:G8:b::2:::l:3:f:10\ncell:A9:b::2:::l:3:f:10\ncell:B9:b::::2:l:3:f:7\ncell:C9:t:Email\\c:f:9:cf:2:colspan:3\ncell:E9:f:1:cf:2\ncell:G9:b::2:::l:3:f:10\ncell:A10:b::2:::l:3:f:10\ncell:B10:b::::2:l:3:f:7\ncell:C10:colspan:2\ncell:G10:b::2:::l:3:f:10\ncell:A11:b::2:::l:3:f:10\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:FROM\\c:f:9:colspan:2\ncell:E11:f:8:colspan:2\ncell:G11:b::2:::l:3:f:10\ncell:A12:b::2:::l:3:f:10\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[Name]:f:9:colspan:4\ncell:E12:colspan:2\ncell:G12:b::2:::l:3:f:10\ncell:A13:b::2:::l:3:f:10\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:[Street Address]:f:9:colspan:4\ncell:E13:colspan:2\ncell:G13:b::2:::l:3:f:10\ncell:A14:b::2:::l:3:f:10\ncell:B14:b::::2:l:3:f:7\ncell:C14:t:[City, State, Zip]:f:9:colspan:4\ncell:E14:colspan:2\ncell:G14:b::2:::l:3:f:10\ncell:A15:b::2:::l:3:f:10\ncell:B15:b::::2:l:3:f:7\ncell:C15:t:Phone\\c:f:9:colspan:4\ncell:D15:f:8:cf:2:colspan:3\ncell:E15:colspan:2\ncell:G15:b::2:::l:3:f:10\ncell:A16:b::2:::l:3:f:10\ncell:B16:b::::2:l:3:f:7\ncell:C16:t:Email\\c:f:9:colspan:4\ncell:E16:colspan:2\ncell:G16:b::2:::l:3:f:10\ncell:A17:b::2:::l:3:f:10\ncell:B17:b::::2:l:3:f:6\ncell:G17:b::2:::l:3:f:10\ncell:A18:b::2:::l:3:f:10\ncell:B18:b::::2:l:3:f:10\ncell:C18:t:INVOICE #\\c:f:9:cf:2:colspan:4\ncell:D18:v:1:l:4:f:9:cf:2:colspan:3\ncell:G18:b::2:::l:3:f:10\ncell:A19:b::2:::l:3:f:10\ncell:B19:b::::2:l:3:f:10\ncell:G19:b::2:::l:3:f:10\ncell:A20:b::2:::l:3:f:10\ncell:B20:b::::2:l:3:f:10\ncell:C20:t:DATE\\c:f:9:cf:2\ncell:D20:vtf:nd:42081:TODAY():f:9:cf:2:ntvf:3:colspan:2\ncell:G20:b::2:::l:3:f:10\ncell:A21:b::2:::l:3:f:10\ncell:B21:b::::2:l:3:f:6:cf:2\ncell:C21:b:::2::l:1:f:10\ncell:D21:b:::2::l:3:f:6:cf:2\ncell:E21:b:::2::l:1:f:10\ncell:F21:b:::2::l:1:f:10\ncell:G21:b::2:::l:3:f:10\ncell:A22:b::2:::l:3:f:10\ncell:B22:b::2::2:l:3:f:10\ncell:C22:t:Description:b:1::1:1:f:9:cf:1:colspan:3\ncell:D22:t:Description:b:1::1::l:3:f:6:cf:2:colspan:2:rowspan:1\ncell:E22:t::b:2:2:2::l:1:f:10\ncell:F22:t:Amount:b:1:1:1:1:f:9:cf:1\ncell:G22:b::2::2:l:3:f:10\ncell:A23:b::2:::l:3:f:10\ncell:B23:b::2::2:l:3:f:10\ncell:C23:b:1:1::1:f:2:cf:2:colspan:3\ncell:D23:b:1:1:::l:3:f:10:cf:2:colspan:2:rowspan:1\ncell:E23:t::b:2:2:2::l:1:f:10\ncell:F23:b:1:1:::f:2:ntvf:1\ncell:G23:b::2::2:l:3:f:10\ncell:A24:b::2:::l:3:f:10\ncell:B24:b::2::2:l:3:f:10:cf:2\ncell:C24:b::1::1:f:2:cf:2:colspan:3\ncell:D24:b::1:::l:1:f:10:colspan:2\ncell:E24:l:1:f:10\ncell:F24:b::1:::f:2:ntvf:1\ncell:G24:b::2::2:l:3:f:10\ncell:A25:b::2:::l:3:f:10\ncell:B25:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C25:b::1::1:f:2:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:2:ntvf:1\ncell:G25:b::2::2:l:3:f:10\ncell:A26:b::2:::l:3:f:10\ncell:B26:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C26:b::1::1:f:2:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:2:ntvf:1\ncell:G26:b::2::2:l:3:f:10\ncell:A27:b::2:::l:3:f:10\ncell:B27:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C27:b::1::1:f:2:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:2:ntvf:1\ncell:G27:b::2::2:l:3:f:10\ncell:A28:b::2:::l:3:f:10\ncell:B28:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C28:b::1::1:f:2:cf:2:colspan:3\ncell:D28:b::1:::colspan:2\ncell:F28:b::1:::f:2:ntvf:1\ncell:G28:b::2::2:l:3:f:10\ncell:A29:b::2:::l:3:f:10\ncell:B29:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C29:b::1::1:f:2:cf:2:colspan:3\ncell:D29:b::1:::colspan:2\ncell:F29:b::1:::f:2:ntvf:1\ncell:G29:b::2::2:l:3:f:10\ncell:A30:b::2:::l:3:f:10\ncell:B30:b::2::2:l:3:f:10:cf:2\ncell:C30:b::1::1:f:2:cf:2:colspan:3\ncell:D30:b::1:::colspan:2\ncell:F30:b::1:::f:2:ntvf:1\ncell:G30:b::2::2:l:3:f:10\ncell:A31:b::2:::l:3:f:10\ncell:B31:b::2::2:l:3:f:10:cf:2\ncell:C31:b::1::1:f:2:cf:2:colspan:3\ncell:D31:b::1:::colspan:2\ncell:F31:b::1:::f:2:ntvf:1\ncell:G31:b::2::2:l:3:f:10\ncell:A32:b::2:::l:3:f:10\ncell:B32:b::2::2:l:3:f:10:cf:2\ncell:C32:b::1::1:f:2:cf:2:colspan:3\ncell:D32:b::1:::colspan:2\ncell:F32:b::1:::f:2:ntvf:1\ncell:G32:b::2::2:l:3:f:10\ncell:A33:b::2:::l:3:f:10\ncell:B33:b::2::2:l:3:f:10:cf:2\ncell:C33:b::1::1:f:2:cf:2:colspan:3\ncell:D33:b::1:::colspan:2\ncell:F33:b::1:::f:2:ntvf:1\ncell:G33:b::2::2:l:3:f:10\ncell:A34:b::2:::l:3:f:10\ncell:B34:b::2::2:l:3:f:10:cf:2\ncell:C34:b::1::1:f:2:cf:2:colspan:3\ncell:D34:b::1:::colspan:2\ncell:F34:b::1:::f:2:ntvf:1\ncell:G34:b::2::2:l:3:f:10\ncell:A35:b::2:::l:3:f:7\ncell:B35:b::2::2:l:3:f:10:cf:2\ncell:C35:b::1:1:1:f:2:cf:2:colspan:3\ncell:D35:b::1:1::l:3:f:10:cf:2:colspan:2\ncell:E35:b:::2::l:3:f:10:cf:2\ncell:F35:b::1:1::f:2:ntvf:1\ncell:G35:b::2::2:l:3:f:10\ncell:A36:b::2:::l:3:f:6\ncell:B36:b::2::2:l:3:f:6\ncell:C36:t:TOTAL:b:1:1:1:1:f:11:cf:2:colspan:3\ncell:D36:b:2::2::l:3:f:12:cf:2\ncell:E36:b:2:2:2::l:3:f:12:cf:2\ncell:F36:vtf:n:0:SUM(F23\\cF35):b:1:1:1::f:11:ntvf:1\ncell:G36:b::2::2:l:3:f:7\ncell:A37:b::2:::l:3:f:10\ncell:B37:b::::2:l:3:f:6:cf:2\ncell:C37:b:2::::l:1:f:10\ncell:D37:b:2::::l:1:f:10\ncell:E37:b:2::::l:1:f:10\ncell:F37:b:2::::l:1:f:10\ncell:G37:b::2:::l:3:f:6\ncell:A38:b::2:::l:3:f:10\ncell:B38:b::::2:l:3:f:10\ncell:G38:b::2:::l:3:f:10\ncell:A39:b::2:::l:3:f:10\ncell:B39:b::::2:l:3:f:10:cf:2\ncell:C39:t:Thank you for your business:f:4:cf:1:colspan:4\ncell:D39:t:Thank you for your business:colspan:3\ncell:E39:t:Thank you for your business:f:3:colspan:2\ncell:F39:t::l:2:f:10\ncell:G39:b::2:::l:3:f:10\ncell:A40:b::2:::l:3:f:10\ncell:B40:b::::2:l:3:f:10\ncell:G40:b::2:::l:3:f:10\ncell:A41:b::2:::l:3:f:10\ncell:B41:b:::2:2:l:3:f:10\ncell:C41:b:::2::l:3:f:10\ncell:D41:b:::2::l:3:f:10\ncell:E41:b:::2::l:3:f:10\ncell:F41:b:::2::l:3:f:10\ncell:G41:b::2:2::l:3:f:10\ncell:B42:b:1:::\ncell:C42:b:1:::\ncell:D42:b:1:::\ncell:E42:b:1:::\ncell:F42:b:1:::\ncell:G42:b:1:::\ncol:A:w:10\ncol:B:w:10\ncol:C:w:62\ncol:D:w:73\ncol:E:w:15\ncol:F:w:68\ncol:G:w:10\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:15.75\nrow:36:h:15.75\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:41:h:14.25\nsheet:c:7:r:42:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:* 9pt Trebuchet MS\nfont:3:italic bold * Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Arial\nfont:7:normal bold 12pt Arial\nfont:8:normal normal * *\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 11pt Trebuchet MS\nfont:12:normal normal 12pt Arial\nfont:13:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nlayout:4:padding:* * * 4px;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", }, name: "inv1", hidden: "0", @@ -19,7 +19,7 @@ export let DATA = { sheet2: { sheetstr: { savestr: - "version:1.5\ncell:B2:t:INVOICE:l:1:f:9:cf:1:colspan:6\ncell:C2:t::l:2:f:13\ncell:D2:t::l:2:f:13\ncell:E2:l:1:f:10:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:8:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:13\ncell:B3:f:6:cf:2:colspan:4\ncell:C3:t::l:2:f:13\ncell:D3:t::l:2:f:13\ncell:F3:l:1:f:7:cf:2\ncell:G3:l:1:f:14:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:14:cf:2\ncell:G4:vtf:nd:45860:TODAY():l:1:f:14:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:3:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:7:cf:2:colspan:2\ncell:G5:l:1:f:14:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:7:cf:2:colspan:2\ncell:G6:l:1:f:14:cf:2\ncell:B7:t:FROM\\c:f:12\ncell:F7:l:1:f:7:cf:2:tvf:4:colspan:2:rowspan:6\ncell:G7:l:1:f:14:cf:2\ncell:B8:t:[Company Name]:f:3:colspan:4\ncell:F8:l:1:f:7:cf:2:colspan:2\ncell:G8:l:1:f:14:cf:2\ncell:B9:t:[Street Address]:f:1:cf:2:colspan:4\ncell:F9:l:1:f:7\ncell:G9:l:1:f:14:cf:1\ncell:B10:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:G10:l:1:f:13\ncell:B11:t:Phone\\c :f:1:cf:2:colspan:4\ncell:B12:t:Email\\c:f:1:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:11:cf:2\ncell:B15:t:[Name]:f:1:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:1:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:1:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:1:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:1:cf:2:colspan:6\ncell:B20:t:Email\\c:f:1:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:14:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:13\ncell:D22:t::l:2:f:13\ncell:E22:t::l:2:f:13\ncell:F22:t::l:2:f:13\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:14:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:b:1:1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:4\ncell:D23:t::l:2:f:4\ncell:E23:t::l:2:f:4\ncell:F23:t::b::2:::l:1:f:4\ncell:G23:b::1::1:f:4:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:4\ncell:D24:t::l:2:f:4\ncell:E24:t::l:2:f:4\ncell:F24:t::b::2:::l:1:f:4\ncell:G24:b::1::1:f:4:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:4\ncell:D25:t::l:2:f:4\ncell:E25:t::l:2:f:4\ncell:F25:t::b::2:::l:1:f:4\ncell:G25:b::1::1:f:4:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:4\ncell:D26:t::l:2:f:4\ncell:E26:t::l:2:f:4\ncell:F26:t::b::2:::l:1:f:4\ncell:G26:b::1::1:f:4:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:4\ncell:D27:t::l:2:f:4\ncell:E27:t::l:2:f:4\ncell:F27:t::b::2:::l:1:f:4\ncell:G27:b::1::1:f:4:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:4\ncell:D28:t::l:2:f:4\ncell:E28:t::l:2:f:4\ncell:F28:t::b::2:::l:1:f:4\ncell:G28:b::1::1:f:4:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:4\ncell:D29:t::l:2:f:4\ncell:E29:t::l:2:f:4\ncell:F29:t::b::2:::l:1:f:4\ncell:G29:b::1::1:f:4:ntvf:1\ncell:A30:b::1::\ncell:B30:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:4\ncell:D30:t::l:2:f:4\ncell:E30:t::l:2:f:4\ncell:F30:t::b::2:::l:1:f:4\ncell:G30:b::1::1:f:4:ntvf:1\ncell:A31:b::1::\ncell:B31:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:4\ncell:D31:t::l:2:f:4\ncell:E31:t::l:2:f:4\ncell:F31:t::b::2:::l:1:f:4\ncell:G31:b::1::1:f:4:ntvf:1\ncell:A32:b::1::\ncell:B32:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:4\ncell:D32:t::l:2:f:4\ncell:E32:t::l:2:f:4\ncell:F32:t::b::2:::l:1:f:4\ncell:G32:b::1::1:f:4:ntvf:1\ncell:A33:b::1::\ncell:B33:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C33:t::l:2:f:4\ncell:D33:t::l:2:f:4\ncell:E33:t::l:2:f:4\ncell:F33:t::b::2:::l:1:f:4\ncell:G33:b::1::1:f:4:ntvf:1\ncell:A34:b::1::\ncell:B34:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C34:l:2:f:4\ncell:D34:l:2:f:4\ncell:E34:l:2:f:4\ncell:F34:b::2:::l:1:f:4\ncell:G34:b::1::1:f:4:ntvf:1\ncell:A35:b::1::\ncell:B35:b::1:1:1:f:4:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:4\ncell:D35:t::b:::2::l:1:f:4\ncell:E35:t::b:::2::l:1:f:4\ncell:F35:t::b::2:2::l:1:f:4\ncell:G35:b::1:1:1:f:4:ntvf:1\ncell:B36:b:2::::l:1:f:13\ncell:C36:b:2::::l:1:f:13\ncell:D36:b:2::::l:1:f:13\ncell:E36:b:2::::l:1:f:14\ncell:F36:t:Subtotal:b:2::::l:1:f:11\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:11:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:14:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:13\ncell:D37:t::b:::2::l:1:f:13\ncell:F37:t:Tax Rate:l:1:f:11\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:13\ncell:D38:t::b:2::::l:1:f:13\ncell:F38:t:Tax:l:1:f:11\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:13\ncell:D39:t::l:2:f:13\ncell:F39:t:Other:b:::1::l:1:f:11\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:13\ncell:D40:t::l:2:f:13\ncell:F40:t:TOTAL:b:1::::l:1:f:14\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:11:ntvf:1\ncell:C42:colspan:5:rowspan:3\ncell:B43:l:1:f:5:cf:1\ncell:C43:t::l:2:f:13\ncell:D43:t::l:2:f:13\ncell:E43:t::l:2:f:13\ncell:F43:t::l:2:f:13\ncell:G43:t::l:2:f:13\ncol:A:w:10\ncol:B:w:90\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:45\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 10pt Trebuchet MS\nfont:4:* 9pt Trebuchet MS\nfont:5:italic bold 10pt Trebuchet MS\nfont:6:italic normal * Trebuchet MS\nfont:7:normal bold 10pt Trebuchet MS\nfont:8:normal bold 14pt Trebuchet MS\nfont:9:normal bold 16pt Trebuchet MS\nfont:10:normal bold 28pt Trebuchet MS\nfont:11:normal normal * Trebuchet MS\nfont:12:normal normal 10pt *\nfont:13:normal normal 10pt Arial\nfont:14:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + "version:1.5\ncell:B2:t:INVOICE:l:1:f:9:cf:1:colspan:6\ncell:C2:t::l:2:f:13\ncell:D2:t::l:2:f:13\ncell:E2:l:1:f:10:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:8:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:13\ncell:B3:f:6:cf:2:colspan:4\ncell:C3:t::l:2:f:13\ncell:D3:t::l:2:f:13\ncell:F3:l:1:f:7:cf:2\ncell:G3:l:1:f:14:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:14:cf:2\ncell:G4:vtf:nd:42081:TODAY():l:1:f:14:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:3:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:7:cf:2:colspan:2\ncell:G5:l:1:f:14:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:7:cf:2:colspan:2\ncell:G6:l:1:f:14:cf:2\ncell:B7:t:FROM\\c:f:12\ncell:F7:l:1:f:7:cf:2:tvf:4:colspan:2:rowspan:6\ncell:G7:l:1:f:14:cf:2\ncell:B8:t:[Company Name]:f:3:colspan:4\ncell:F8:l:1:f:7:cf:2:colspan:2\ncell:G8:l:1:f:14:cf:2\ncell:B9:t:[Street Address]:f:1:cf:2:colspan:4\ncell:F9:l:1:f:7\ncell:G9:l:1:f:14:cf:1\ncell:B10:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:G10:l:1:f:13\ncell:B11:t:Phone\\c :f:1:cf:2:colspan:4\ncell:B12:t:Email\\c:f:1:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:11:cf:2\ncell:B15:t:[Name]:f:1:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:1:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:1:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:1:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:1:cf:2:colspan:6\ncell:B20:t:Email\\c:f:1:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:14:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:13\ncell:D22:t::l:2:f:13\ncell:E22:t::l:2:f:13\ncell:F22:t::l:2:f:13\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:14:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:b:1:1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:4\ncell:D23:t::l:2:f:4\ncell:E23:t::l:2:f:4\ncell:F23:t::b::2:::l:1:f:4\ncell:G23:b::1::1:f:4:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:4\ncell:D24:t::l:2:f:4\ncell:E24:t::l:2:f:4\ncell:F24:t::b::2:::l:1:f:4\ncell:G24:b::1::1:f:4:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:4\ncell:D25:t::l:2:f:4\ncell:E25:t::l:2:f:4\ncell:F25:t::b::2:::l:1:f:4\ncell:G25:b::1::1:f:4:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:4\ncell:D26:t::l:2:f:4\ncell:E26:t::l:2:f:4\ncell:F26:t::b::2:::l:1:f:4\ncell:G26:b::1::1:f:4:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:4\ncell:D27:t::l:2:f:4\ncell:E27:t::l:2:f:4\ncell:F27:t::b::2:::l:1:f:4\ncell:G27:b::1::1:f:4:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:4\ncell:D28:t::l:2:f:4\ncell:E28:t::l:2:f:4\ncell:F28:t::b::2:::l:1:f:4\ncell:G28:b::1::1:f:4:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:4\ncell:D29:t::l:2:f:4\ncell:E29:t::l:2:f:4\ncell:F29:t::b::2:::l:1:f:4\ncell:G29:b::1::1:f:4:ntvf:1\ncell:A30:b::1::\ncell:B30:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:4\ncell:D30:t::l:2:f:4\ncell:E30:t::l:2:f:4\ncell:F30:t::b::2:::l:1:f:4\ncell:G30:b::1::1:f:4:ntvf:1\ncell:A31:b::1::\ncell:B31:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:4\ncell:D31:t::l:2:f:4\ncell:E31:t::l:2:f:4\ncell:F31:t::b::2:::l:1:f:4\ncell:G31:b::1::1:f:4:ntvf:1\ncell:A32:b::1::\ncell:B32:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:4\ncell:D32:t::l:2:f:4\ncell:E32:t::l:2:f:4\ncell:F32:t::b::2:::l:1:f:4\ncell:G32:b::1::1:f:4:ntvf:1\ncell:A33:b::1::\ncell:B33:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C33:t::l:2:f:4\ncell:D33:t::l:2:f:4\ncell:E33:t::l:2:f:4\ncell:F33:t::b::2:::l:1:f:4\ncell:G33:b::1::1:f:4:ntvf:1\ncell:A34:b::1::\ncell:B34:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C34:l:2:f:4\ncell:D34:l:2:f:4\ncell:E34:l:2:f:4\ncell:F34:b::2:::l:1:f:4\ncell:G34:b::1::1:f:4:ntvf:1\ncell:A35:b::1::\ncell:B35:b::1:1:1:f:4:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:4\ncell:D35:t::b:::2::l:1:f:4\ncell:E35:t::b:::2::l:1:f:4\ncell:F35:t::b::2:2::l:1:f:4\ncell:G35:b::1:1:1:f:4:ntvf:1\ncell:B36:b:2::::l:1:f:13\ncell:C36:b:2::::l:1:f:13\ncell:D36:b:2::::l:1:f:13\ncell:E36:b:2::::l:1:f:14\ncell:F36:t:Subtotal:b:2::::l:1:f:11\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:11:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:14:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:13\ncell:D37:t::b:::2::l:1:f:13\ncell:F37:t:Tax Rate:l:1:f:11\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:13\ncell:D38:t::b:2::::l:1:f:13\ncell:F38:t:Tax:l:1:f:11\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:13\ncell:D39:t::l:2:f:13\ncell:F39:t:Other:b:::1::l:1:f:11\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:13\ncell:D40:t::l:2:f:13\ncell:F40:t:TOTAL:b:1::::l:1:f:14\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:11:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:5:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:13\ncell:D43:t::l:2:f:13\ncell:E43:t::l:2:f:13\ncell:F43:t::l:2:f:13\ncell:G43:t::l:2:f:13\ncol:A:w:10\ncol:B:w:90\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:45\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 10pt Trebuchet MS\nfont:4:* 9pt Trebuchet MS\nfont:5:italic bold 10pt Trebuchet MS\nfont:6:italic normal * Trebuchet MS\nfont:7:normal bold 10pt Trebuchet MS\nfont:8:normal bold 14pt Trebuchet MS\nfont:9:normal bold 16pt Trebuchet MS\nfont:10:normal bold 28pt Trebuchet MS\nfont:11:normal normal * Trebuchet MS\nfont:12:normal normal 10pt *\nfont:13:normal normal 10pt Arial\nfont:14:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", }, name: "inv2", hidden: "0", @@ -27,25 +27,25 @@ export let DATA = { sheet3: { sheetstr: { savestr: - 'version:1.5\ncell:B2:t:INVOICE:l:1:f:12:cf:1:colspan:6\ncell:C2:t::l:2:f:10\ncell:D2:t::l:2:f:10\ncell:E2:l:1:f:8:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:10\ncell:B3:f:5:cf:2:colspan:4\ncell:C3:t::l:2:f:10\ncell:D3:t::l:2:f:10\ncell:F3:l:1:f:6:cf:2\ncell:G3:l:1:f:11:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:11:cf:2\ncell:G4:vtf:nd:45860:TODAY():l:1:f:11:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:9:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:6:cf:2:colspan:2\ncell:G5:l:1:f:11:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:6:cf:2:colspan:2\ncell:G6:l:1:f:11:cf:2\ncell:B7:t:FROM\\c:f:11\ncell:F7:l:1:f:6:cf:2:colspan:2\ncell:G7:l:1:f:11:cf:2\ncell:B8:t:[Company Name]:f:9:colspan:4\ncell:F8:l:1:f:6:cf:2:tvf:4:colspan:2:rowspan:5\ncell:G8:l:1:f:11:cf:2\ncell:B9:t:[Street Address]:f:9:cf:2:colspan:4\ncell:F9:l:1:f:6\ncell:G9:l:1:f:11:cf:1\ncell:B10:t:[City, State, Zip]:f:9:cf:2:colspan:4\ncell:G10:l:1:f:10\ncell:B11:t:Phone\\c :f:9:cf:2:colspan:4\ncell:B12:t:Email\\c:f:9:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:9:cf:2\ncell:B15:t:[Name]:f:9:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:9:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:9:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:9:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:9:cf:2:colspan:6\ncell:B20:t:Email\\c:f:9:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:11:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:10\ncell:D22:t::l:2:f:10\ncell:E22:t::l:2:f:10\ncell:F22:t::l:2:f:10\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:11:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:vtf:e#REF!:0:IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), sheet7!B6, inv3!B6)):e:#REF!:b:1:1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:3\ncell:D23:t::l:2:f:3\ncell:E23:t::l:2:f:3\ncell:F23:t::b::2:::l:1:f:3\ncell:G23:vtf:e#REF!:0:IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), (sheet7!E6*sheet7!F6), (inv3!E6*inv3!F6))):e:#REF!:b::1::1:f:3:ntvf:1\ncell:A24:b::1::\ncell:B24:vtf:t:FROM\\c:IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), SHEET7!B7, INV3!B7)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:3\ncell:D24:t::l:2:f:3\ncell:E24:t::l:2:f:3\ncell:F24:t::b::2:::l:1:f:3\ncell:G24:vtf:n:0:IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), (SHEET7!E7*SHEET7!F7), (INV3!E7*INV3!F7))):b::1::1:f:3:ntvf:1\ncell:A25:b::1::\ncell:B25:vtf:t:[Company Name]:IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), SHEET7!B8, INV3!B8)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:3\ncell:D25:t::l:2:f:3\ncell:E25:t::l:2:f:3\ncell:F25:t::b::2:::l:1:f:3\ncell:G25:vtf:n:0:IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), (SHEET7!E8*SHEET7!F8), (INV3!E8*INV3!F8))):b::1::1:f:3:ntvf:1\ncell:A26:b::1::\ncell:B26:vtf:t:[Street Address]:IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), SHEET7!B9, INV3!B9)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:3\ncell:D26:t::l:2:f:3\ncell:E26:t::l:2:f:3\ncell:F26:t::b::2:::l:1:f:3\ncell:G26:vtf:n:0:IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), (SHEET7!E9*SHEET7!F9), (INV3!E9*INV3!F9))):b::1::1:f:3:ntvf:1\ncell:A27:b::1::\ncell:B27:vtf:t:[City, State, Zip]:IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), SHEET7!B10, INV3!B10)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:3\ncell:D27:t::l:2:f:3\ncell:E27:t::l:2:f:3\ncell:F27:t::b::2:::l:1:f:3\ncell:G27:vtf:n:0:IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), (SHEET7!E10*SHEET7!F10), (INV3!E10*INV3!F10))):b::1::1:f:3:ntvf:1\ncell:A28:b::1::\ncell:B28:vtf:t:Phone\\c :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), SHEET7!B11, INV3!B11)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:3\ncell:D28:t::l:2:f:3\ncell:E28:t::l:2:f:3\ncell:F28:t::b::2:::l:1:f:3\ncell:G28:vtf:n:0:IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), (SHEET7!E11*SHEET7!F11), (INV3!E11*INV3!F11))):b::1::1:f:3:ntvf:1\ncell:A29:b::1::\ncell:B29:vtf:t:Email\\c:IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), SHEET7!B12, INV3!B12)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:3\ncell:D29:t::l:2:f:3\ncell:E29:t::l:2:f:3\ncell:F29:t::b::2:::l:1:f:3\ncell:G29:vtf:n:0:IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), (SHEET7!E12*SHEET7!F12), (INV3!E12*INV3!F12))):b::1::1:f:3:ntvf:1\ncell:A30:b::1::\ncell:B30:vtf:e#REF!:0:IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), SHEET7!B13, INV3!B13)):e:#REF!:b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:3\ncell:D30:t::l:2:f:3\ncell:E30:t::l:2:f:3\ncell:F30:t::b::2:::l:1:f:3\ncell:G30:vtf:e#REF!:0:IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), (SHEET7!E13*SHEET7!F13), (INV3!E13*INV3!F13))):e:#REF!:b::1::1:f:3:ntvf:1\ncell:A31:b::1::\ncell:B31:vtf:t:BILL TO\\c:IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), SHEET7!B14, INV3!B14)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:3\ncell:D31:t::l:2:f:3\ncell:E31:t::l:2:f:3\ncell:F31:t::b::2:::l:1:f:3\ncell:G31:vtf:n:0:IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), (SHEET7!E14*SHEET7!F14), (INV3!E14*INV3!F14))):b::1::1:f:3:ntvf:1\ncell:A32:b::1::\ncell:B32:vtf:t:[Name]:IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), SHEET7!B15, INV3!B15)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:3\ncell:D32:t::l:2:f:3\ncell:E32:t::l:2:f:3\ncell:F32:t::b::2:::l:1:f:3\ncell:G32:vtf:n:0:IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), (SHEET7!E15*SHEET7!F15), (INV3!E15*INV3!F15))):b::1::1:f:3:ntvf:1\ncell:A33:b::1::\ncell:B33:vtf:t:[Company Name]:IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), SHEET7!B16, INV3!B16)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C33:l:2:f:3\ncell:D33:l:2:f:3\ncell:E33:l:2:f:3\ncell:F33:b::2:::l:1:f:3\ncell:G33:vtf:e#VALUE!:0:IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), (SHEET7!E16*SHEET7!F16), (INV3!E16*INV3!F16))):e:#VALUE!:b::1::1:f:3:ntvf:1\ncell:A34:b::1::\ncell:B34:vtf:t:[Street Address]:IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), SHEET7!B17, INV3!B17)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C34:t::l:2:f:3\ncell:D34:t::l:2:f:3\ncell:E34:t::l:2:f:3\ncell:F34:t::b::2:::l:1:f:3\ncell:G34:vtf:n:0:IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), (SHEET7!E17*SHEET7!F17), (INV3!E17*INV3!F17))):b::1::1:f:3:ntvf:1\ncell:A35:b::1::\ncell:B35:vtf:t:[City, State, Zip]:IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), SHEET7!B18, INV3!B18)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:3\ncell:D35:t::b:::2::l:1:f:3\ncell:E35:t::b:::2::l:1:f:3\ncell:F35:t::b::2:2::l:1:f:3\ncell:G35:vtf:n:0:IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), (SHEET7!E18*SHEET7!F18), (INV3!E18*INV3!F18))):b::1::1:f:3:ntvf:1\ncell:B36:b:2::::l:1:f:10\ncell:C36:b:2::::l:1:f:10\ncell:D36:b:2::::l:1:f:10\ncell:E36:b:2::::l:1:f:11\ncell:F36:t:Subtotal:b:2::::l:1:f:9\ncell:G36:vtf:e#REF!:0:SUM(G23\\cG35):e:#REF!:b:1::::f:9:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:11:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:10\ncell:D37:t::b:::2::l:1:f:10\ncell:F37:t:Tax Rate:l:1:f:9\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:10\ncell:D38:t::b:2::::l:1:f:10\ncell:F38:t:Tax:l:1:f:9\ncell:G38:vtf:e#REF!:0:G37*G36:e:#REF!:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:10\ncell:D39:t::l:2:f:10\ncell:F39:t:Other:b:::1::l:1:f:9\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:10\ncell:D40:t::l:2:f:10\ncell:F40:t:TOTAL:b:1::::l:1:f:11\ncell:G40:vtf:e#REF!:0:(G36+G38)+G39:e:#REF!:b:1::::f:9:ntvf:1\ncell:C42:colspan:5:rowspan:3\ncell:B43:l:1:f:4:cf:1\ncell:C43:t::l:2:f:10\ncell:D43:t::l:2:f:10\ncell:E43:t::l:2:f:10\ncell:F43:t::l:2:f:10\ncell:G43:t::l:2:f:10\ncol:A:w:10\ncol:B:w:65\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:53\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 9pt Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:italic normal * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 14pt Trebuchet MS\nfont:8:normal bold 28pt Trebuchet MS\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 10pt Trebuchet MS\nfont:12:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', + 'version:1.5\ncell:B2:t:INVOICE:l:1:f:12:cf:1:colspan:6\ncell:C2:t::l:2:f:10\ncell:D2:t::l:2:f:10\ncell:E2:l:1:f:8:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:10\ncell:B3:f:5:cf:2:colspan:4\ncell:C3:t::l:2:f:10\ncell:D3:t::l:2:f:10\ncell:F3:l:1:f:6:cf:2\ncell:G3:l:1:f:11:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:11:cf:2\ncell:G4:vtf:nd:42081:TODAY():l:1:f:11:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:9:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:6:cf:2:colspan:2\ncell:G5:l:1:f:11:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:6:cf:2:colspan:2\ncell:G6:l:1:f:11:cf:2\ncell:B7:t:FROM\\c:f:11\ncell:F7:l:1:f:6:cf:2:colspan:2\ncell:G7:l:1:f:11:cf:2\ncell:B8:t:[Company Name]:f:9:colspan:4\ncell:F8:l:1:f:6:cf:2:tvf:4:colspan:2:rowspan:5\ncell:G8:l:1:f:11:cf:2\ncell:B9:t:[Street Address]:f:9:cf:2:colspan:4\ncell:F9:l:1:f:6\ncell:G9:l:1:f:11:cf:1\ncell:B10:t:[City, State, Zip]:f:9:cf:2:colspan:4\ncell:G10:l:1:f:10\ncell:B11:t:Phone\\c :f:9:cf:2:colspan:4\ncell:B12:t:Email\\c:f:9:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:9:cf:2\ncell:B15:t:[Name]:f:9:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:9:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:9:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:9:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:9:cf:2:colspan:6\ncell:B20:t:Email\\c:f:9:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:11:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:10\ncell:D22:t::l:2:f:10\ncell:E22:t::l:2:f:10\ncell:F22:t::l:2:f:10\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:11:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), sheet7!B6, inv3!B6)):b:1:1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:3\ncell:D23:t::l:2:f:3\ncell:E23:t::l:2:f:3\ncell:F23:t::b::2:::l:1:f:3\ncell:G23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), (sheet7!E6*sheet7!F6), (inv3!E6*inv3!F6))):b::1::1:f:3:ntvf:1\ncell:A24:b::1::\ncell:B24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), SHEET7!B7, INV3!B7)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:3\ncell:D24:t::l:2:f:3\ncell:E24:t::l:2:f:3\ncell:F24:t::b::2:::l:1:f:3\ncell:G24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), (SHEET7!E7*SHEET7!F7), (INV3!E7*INV3!F7))):b::1::1:f:3:ntvf:1\ncell:A25:b::1::\ncell:B25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), SHEET7!B8, INV3!B8)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:3\ncell:D25:t::l:2:f:3\ncell:E25:t::l:2:f:3\ncell:F25:t::b::2:::l:1:f:3\ncell:G25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), (SHEET7!E8*SHEET7!F8), (INV3!E8*INV3!F8))):b::1::1:f:3:ntvf:1\ncell:A26:b::1::\ncell:B26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), SHEET7!B9, INV3!B9)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:3\ncell:D26:t::l:2:f:3\ncell:E26:t::l:2:f:3\ncell:F26:t::b::2:::l:1:f:3\ncell:G26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), (SHEET7!E9*SHEET7!F9), (INV3!E9*INV3!F9))):b::1::1:f:3:ntvf:1\ncell:A27:b::1::\ncell:B27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), SHEET7!B10, INV3!B10)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:3\ncell:D27:t::l:2:f:3\ncell:E27:t::l:2:f:3\ncell:F27:t::b::2:::l:1:f:3\ncell:G27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), (SHEET7!E10*SHEET7!F10), (INV3!E10*INV3!F10))):b::1::1:f:3:ntvf:1\ncell:A28:b::1::\ncell:B28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), SHEET7!B11, INV3!B11)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:3\ncell:D28:t::l:2:f:3\ncell:E28:t::l:2:f:3\ncell:F28:t::b::2:::l:1:f:3\ncell:G28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), (SHEET7!E11*SHEET7!F11), (INV3!E11*INV3!F11))):b::1::1:f:3:ntvf:1\ncell:A29:b::1::\ncell:B29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), SHEET7!B12, INV3!B12)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:3\ncell:D29:t::l:2:f:3\ncell:E29:t::l:2:f:3\ncell:F29:t::b::2:::l:1:f:3\ncell:G29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), (SHEET7!E12*SHEET7!F12), (INV3!E12*INV3!F12))):b::1::1:f:3:ntvf:1\ncell:A30:b::1::\ncell:B30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), SHEET7!B13, INV3!B13)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:3\ncell:D30:t::l:2:f:3\ncell:E30:t::l:2:f:3\ncell:F30:t::b::2:::l:1:f:3\ncell:G30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), (SHEET7!E13*SHEET7!F13), (INV3!E13*INV3!F13))):b::1::1:f:3:ntvf:1\ncell:A31:b::1::\ncell:B31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), SHEET7!B14, INV3!B14)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:3\ncell:D31:t::l:2:f:3\ncell:E31:t::l:2:f:3\ncell:F31:t::b::2:::l:1:f:3\ncell:G31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), (SHEET7!E14*SHEET7!F14), (INV3!E14*INV3!F14))):b::1::1:f:3:ntvf:1\ncell:A32:b::1::\ncell:B32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), SHEET7!B15, INV3!B15)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:3\ncell:D32:t::l:2:f:3\ncell:E32:t::l:2:f:3\ncell:F32:t::b::2:::l:1:f:3\ncell:G32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), (SHEET7!E15*SHEET7!F15), (INV3!E15*INV3!F15))):b::1::1:f:3:ntvf:1\ncell:A33:b::1::\ncell:B33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), SHEET7!B16, INV3!B16)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C33:l:2:f:3\ncell:D33:l:2:f:3\ncell:E33:l:2:f:3\ncell:F33:b::2:::l:1:f:3\ncell:G33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), (SHEET7!E16*SHEET7!F16), (INV3!E16*INV3!F16))):b::1::1:f:3:ntvf:1\ncell:A34:b::1::\ncell:B34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), SHEET7!B17, INV3!B17)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C34:t::l:2:f:3\ncell:D34:t::l:2:f:3\ncell:E34:t::l:2:f:3\ncell:F34:t::b::2:::l:1:f:3\ncell:G34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), (SHEET7!E17*SHEET7!F17), (INV3!E17*INV3!F17))):b::1::1:f:3:ntvf:1\ncell:A35:b::1::\ncell:B35:vtf:t: :IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), SHEET7!B18, INV3!B18)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:3\ncell:D35:t::b:::2::l:1:f:3\ncell:E35:t::b:::2::l:1:f:3\ncell:F35:t::b::2:2::l:1:f:3\ncell:G35:vtf:t: :IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), (SHEET7!E18*SHEET7!F18), (INV3!E18*INV3!F18))):b::1::1:f:3:ntvf:1\ncell:B36:b:2::::l:1:f:10\ncell:C36:b:2::::l:1:f:10\ncell:D36:b:2::::l:1:f:10\ncell:E36:b:2::::l:1:f:11\ncell:F36:t:Subtotal:b:2::::l:1:f:9\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:9:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:11:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:10\ncell:D37:t::b:::2::l:1:f:10\ncell:F37:t:Tax Rate:l:1:f:9\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:10\ncell:D38:t::b:2::::l:1:f:10\ncell:F38:t:Tax:l:1:f:9\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:10\ncell:D39:t::l:2:f:10\ncell:F39:t:Other:b:::1::l:1:f:9\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:10\ncell:D40:t::l:2:f:10\ncell:F40:t:TOTAL:b:1::::l:1:f:11\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:9:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:4:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:10\ncell:D43:t::l:2:f:10\ncell:E43:t::l:2:f:10\ncell:F43:t::l:2:f:10\ncell:G43:t::l:2:f:10\ncol:A:w:10\ncol:B:w:65\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:53\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 9pt Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:italic normal * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 14pt Trebuchet MS\nfont:8:normal bold 28pt Trebuchet MS\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 10pt Trebuchet MS\nfont:12:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', }, - name: "inv3", + name: "sheet6", hidden: "0", }, sheet4: { sheetstr: { savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Hours:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Rate:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Hours:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Rate:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", }, - name: "inv4", + name: "inv3", hidden: "0", }, sheet5: { sheetstr: { savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Qty.:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Price:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Qty.:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Price:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", }, - name: "inv5", + name: "sheet7", hidden: "0", }, }, diff --git a/src/components/Forms/android-inv-Type1.tsx b/src/components/Forms/android-inv-Type1.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index a7f3a56..f4d7663 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -1,47 +1,66 @@ -import React from 'react'; +import React, { useState } from "react"; import { IonBadge, IonIcon, IonItem, IonLabel, - IonNote -} from '@ionic/react'; -import { cloudOfflineOutline, cloudDoneOutline, wifiOutline } from 'ionicons/icons'; -import { usePWA } from '../hooks/usePWA'; + IonNote, + IonButton, +} from "@ionic/react"; +import { + cloudOfflineOutline, + cloudDoneOutline, + wifiOutline, + closeOutline, +} from "ionicons/icons"; +import { usePWA } from "../hooks/usePWA"; const OfflineIndicator: React.FC = () => { const { isOnline } = usePWA(); + const [isDismissed, setIsDismissed] = useState(false); + + // Reset dismissal when coming back online + React.useEffect(() => { + if (isOnline) { + setIsDismissed(false); + } + }, [isOnline]); + + // Don't show if dismissed or if online + if (isDismissed || isOnline) { + return null; + } return ( - - + + -

{isOnline ? 'Online' : 'Offline'}

-

- {isOnline - ? 'All features available' - : 'Some features may be limited' - } -

+

Offline

+

Some features may be limited

- + No Connection + + setIsDismissed(true)} slot="end" > - {isOnline ? 'Connected' : 'No Connection'} - + +
); }; -export default OfflineIndicator; \ No newline at end of file +export default OfflineIndicator; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index e0d48bd..548cf12 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -95,8 +95,8 @@ const Home: React.FC = () => { const [showInvoiceForm, setShowInvoiceForm] = useState(false); // Error state for initialization failures - const [initError, setInitError] = useState(false); - const [fileIsEmpty, setFileIsEmpty] = useState(false); + // const [initError, setInitError] = useState(false); + // const [fileIsEmpty, setFileIsEmpty] = useState(false); // Available colors for sheet themes const availableColors = [ @@ -155,9 +155,6 @@ const Home: React.FC = () => { } }, 100); } - setShowColorModal(false); - setToastColor("success"); - setShowToast(true); } catch (error) { console.error("Error changing sheet color:", error); setToastMessage("Failed to change sheet color"); @@ -188,44 +185,6 @@ const Home: React.FC = () => { setShowColorModal(true); }; - const handleRefreshFile = async () => { - try { - setInitError(false); - setFileIsEmpty(false); - - // Clear the existing content - const container = document.getElementById("container"); - if (container) { - const workbookControl = document.getElementById("workbookControl"); - const tableeditor = document.getElementById("tableeditor"); - const msg = document.getElementById("msg"); - - if (workbookControl) workbookControl.innerHTML = ""; - if (tableeditor) tableeditor.innerHTML = ""; - if (msg) msg.innerHTML = ""; - } - - // Re-initialize with template data - const data = DATA["home"]["App"]["msc"]; - AppGeneral.initializeApp(JSON.stringify(data)); - - // Save the refreshed template as the default file - const initialContent = encodeURIComponent(JSON.stringify(data)); - const now = new Date().toISOString(); - const file = new File(now, now, initialContent, "default", billType); - await store._saveFile(file); - - setToastMessage("File refreshed successfully!"); - setToastColor("success"); - setShowToast(true); - } catch (error) { - console.error("Error refreshing file:", error); - setToastMessage("Failed to refresh file. Please try again."); - setToastColor("danger"); - setShowToast(true); - } - }; - const executeSaveAsWithFilename = async (filename: string) => { updateSelectedFile(filename); @@ -263,26 +222,15 @@ const Home: React.FC = () => { useEffect(() => { const initializeApp = async () => { try { - setInitError(false); - setFileIsEmpty(false); - // First try to load the default file from local storage const defaultExists = await store._checkKey("default"); if (defaultExists) { const defaultFile = await store._getFile("default"); const decodedContent = decodeURIComponent(defaultFile.content); - // Check if the file is empty using the helper function - const isEmpty = isDefaultFileEmpty(decodedContent); - - if (isEmpty) { - setFileIsEmpty(true); - console.log("Default file is empty, showing refresh option"); - } else { - AppGeneral.viewFile("default", decodedContent); - updateBillType(defaultFile.billType); - console.log("Loaded existing default file from local storage"); - } + AppGeneral.viewFile("default", decodedContent); + updateBillType(defaultFile.billType); + console.log("Loaded existing default file from local storage"); } else { // If no default file exists, initialize with template data and save it const data = DATA["home"]["App"]["msc"]; @@ -298,51 +246,15 @@ const Home: React.FC = () => { } catch (error) { console.error("Error initializing app:", error); - // Check if this is the specific workbook error and if file is empty - const isWorkbookError = - error.message && - error.message.includes( - "Cannot read properties of null (reading 'workbook')" - ); - - if (isWorkbookError) { - try { - // Check if the current file is empty - const defaultExists = await store._checkKey("default"); - if (defaultExists) { - const defaultFile = await store._getFile("default"); - const decodedContent = decodeURIComponent(defaultFile.content); - const isEmpty = isDefaultFileEmpty(decodedContent); - - if (isEmpty) { - setInitError(true); - setFileIsEmpty(true); - console.log( - "Workbook error with empty file, showing refresh button" - ); - return; // Don't proceed with fallback initialization - } - } - } catch (checkError) { - console.error("Error checking file emptiness:", checkError); - } - } - // Check if the error is due to storage quota exceeded if (isQuotaExceededError(error)) { setToastMessage(getQuotaExceededMessage("initializing the app")); setToastColor("danger"); setShowToast(true); } - - // Fallback to template initialization for non-workbook errors - if (!isWorkbookError) { - const data = DATA["home"]["App"]["msc"]; - AppGeneral.initializeApp(JSON.stringify(data)); - AppGeneral.changeSheetColor("#000000"); - } else { - setInitError(true); - } + const data = DATA["home"]["App"]["msc"]; + AppGeneral.initializeApp(JSON.stringify(data)); + AppGeneral.changeSheetColor("#000000"); } // Alternative smooth scrolling implementation setTimeout(() => { @@ -575,30 +487,6 @@ const Home: React.FC = () => { slot="end" className={isPlatform("desktop") && "ion-padding-end"} > - {/* PWA Status Indicators */} - - {isInstallable && !isInstalled && ( - - )} {/* Wallet Connection */}
{/* */} @@ -646,60 +534,9 @@ const Home: React.FC = () => {
- {initError && fileIsEmpty ? ( -
- -

- Spreadsheet Failed to Load -

-

- The file appears to be empty or corrupted. Click refresh to - reload with a fresh template. -

- - - Refresh File - -
- ) : ( - <> -
-
-
- - )} +
+
+
{/* Toast for save notifications */} diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 3362317..a142110 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -880,6 +880,135 @@ const SettingsPage: React.FC = () => { } >
+
+ {/* Settings Card */} + + + + + Preferences + + + + + + + Dark Mode + toggleDarkMode()} + slot="end" + /> + + + + +

Reset Onboarding

+

Show landing page on next visit

+
+
+
+
+
+
+ + {/* PWA Status Card */} +
+ + + + + App Status + + + + + + + +

Connection Status

+

{isOnline ? "Online" : "Offline"}

+
+
+ + {isInstallable && !isInstalled && ( + + + +

Install App

+

Install as a Progressive Web App

+
+
+ )} + + {isInstalled && ( + + + +

App Installed

+

Running as installed PWA

+
+
+ )} +
+
+
+
+ {/* Signature Section */}
{
- - - - Menu & Settings - - - - - - - - -
- {/* Settings Card */} - - - - - Preferences - - - - - - - Dark Mode - toggleDarkMode()} - slot="end" - /> - - - - -

Reset Onboarding

-

Show landing page on next visit

-
-
-
-
-
- - {/* PWA Status Card */} - - - - - PWA Features - - - - - - - -

App Installation

-

- {isInstalled - ? "✓ App is installed" - : isInstallable - ? "Ready to install - Click to add to home screen" - : "Installation not available (may already be installed)"} -

-
- {isInstallable && !isInstalled && ( - { - const success = await installApp(); - if (success) { - setToastMessage("App installed successfully!"); - setShowToast(true); - } - }} - slot="end" - > - Install - - )} -
- - - - -

Connection Status

-

- {isOnline - ? "✓ Online - All features available" - : "⚠ Offline - Limited functionality"} -

-
-
- - - - -

Push Notifications

-

- {notificationPermission === "granted" - ? "✓ Enabled - You'll receive updates" - : "Enable notifications for app updates"} -

-
- {notificationPermission !== "granted" && ( - - Enable - - )} -
- - - - -

Offline Storage

-

✓ Your data is saved locally and syncs when online

-
-
- - - - -

Auto Updates

-

✓ App updates automatically in the background

-
-
-
-
-
- - {/* PWA Demo Component */} - -
{/* Menu Component (Action Sheet) */} From 9e7e589c75af6fbb691850a595a5b0cce7f5abf6 Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Mon, 25 Aug 2025 20:24:59 +0530 Subject: [PATCH 3/9] changes --- ARCHITECTURE_CHANGES.md | 86 ++++ ARCHITECTURE_DIAGRAMS.md | 266 ++++++++++++ FEATURE_IMPLEMENTATION_SUMMARY.md | 89 ++++ IMPLEMENTATION_SUMMARY.md | 323 ++++++++++++++ IONALERT_IMPLEMENTATION.md | 115 +++++ MULTI_TEMPLATE_ARCHITECTURE.md | 289 +++++++++++++ TEMPLATE_MIGRATION_SUMMARY.md | 154 +++++++ TEMPLATE_UI_IMPROVEMENTS.md | 88 ++++ TEMPLATE_UI_REDESIGN_SUMMARY.md | 120 ++++++ URL_FILE_EDITING.md | 116 +++++ src/App.tsx | 78 ++-- src/app-data.ts | 474 -------------------- src/components/FileMenu/FileOptions.tsx | 4 +- src/components/Files/Files.tsx | 258 ++++++----- src/components/Menu/Menu.tsx | 10 +- src/components/Storage/LocalStorage.ts | 122 +++++- src/components/TemplateFiles.tsx | 264 ++++++++++++ src/contexts/InvoiceContext.tsx | 23 + src/pages/FilesPage.tsx | 403 +++++++++++------ src/pages/Home.tsx | 240 +++++++---- src/pages/SettingsPage.tsx | 11 + src/templates-meta.ts | 14 + src/templates-new.ts | 189 ++++++++ src/templates.ts | 546 ++++++++++++++++++++++++ src/utils/templateInitializer.ts | 211 +++++++++ src/utils/templateManager.ts | 157 +++++++ 26 files changed, 3772 insertions(+), 878 deletions(-) create mode 100644 ARCHITECTURE_CHANGES.md create mode 100644 ARCHITECTURE_DIAGRAMS.md create mode 100644 FEATURE_IMPLEMENTATION_SUMMARY.md create mode 100644 IMPLEMENTATION_SUMMARY.md create mode 100644 IONALERT_IMPLEMENTATION.md create mode 100644 MULTI_TEMPLATE_ARCHITECTURE.md create mode 100644 TEMPLATE_MIGRATION_SUMMARY.md create mode 100644 TEMPLATE_UI_IMPROVEMENTS.md create mode 100644 TEMPLATE_UI_REDESIGN_SUMMARY.md create mode 100644 URL_FILE_EDITING.md delete mode 100644 src/app-data.ts create mode 100644 src/components/TemplateFiles.tsx create mode 100644 src/templates-meta.ts create mode 100644 src/templates-new.ts create mode 100644 src/templates.ts create mode 100644 src/utils/templateInitializer.ts create mode 100644 src/utils/templateManager.ts diff --git a/ARCHITECTURE_CHANGES.md b/ARCHITECTURE_CHANGES.md new file mode 100644 index 0000000..ffd7e90 --- /dev/null +++ b/ARCHITECTURE_CHANGES.md @@ -0,0 +1,86 @@ +# Architecture Changes: Removed Default File Concept & Updated Navigation + +## Summary +Successfully restructured the application to remove the concept of default files and updated the navigation flow. The files page is now the main landing page, and users must always open a saved file to access the editor. + +## Key Changes Made + +### 1. App.tsx - Navigation Structure +- **Removed**: Tab bar navigation (`IonTabs`, `IonTabBar`, `IonTabButton`) +- **Updated**: Root route now redirects to `/app/files` instead of `/app/editor` +- **Simplified**: Routing structure to use regular `IonRouterOutlet` without tabs + +### 2. FilesPage.tsx - Main Landing Page +- **Added**: Settings button in the header for easy access +- **Removed**: Default file handling logic from `handleNewFileClick()` and `handleNewMedClick()` +- **Removed**: Unsaved changes alert (`showUnsavedChangesAlert`) +- **Removed**: `createNewFile()` and `createNewMed()` functions (replaced by template-specific creation) +- **Cleaned**: Removed `resetToDefaults` dependency + +### 3. Home.tsx (Editor Page) - File-Required Access +- **Added**: Back button in header pointing to files page +- **Added**: `useHistory` for navigation +- **Updated**: Initialization logic to require a selected file +- **Removed**: Default file creation and management +- **Added**: Redirect to files page if no file is selected or file doesn't exist +- **Updated**: Auto-save logic to handle only named files (removed default file handling) +- **Fixed**: Auto-save button visibility (now shows for any selected file) + +### 4. SettingsPage.tsx - Navigation Integration +- **Added**: Back button in header pointing to files page +- **Added**: `useHistory` for navigation +- **Added**: `arrowBack` icon import + +### 5. Files.tsx - File Management +- **Removed**: Default file exclusion filter +- **Simplified**: `handleSaveUnsavedChanges()` function (no longer handles default file) +- **Updated**: File validation to remove "default" file restriction + +### 6. Menu.tsx - Validation Updates +- **Updated**: File name validation to only restrict "Untitled" (removed "default" restriction) + +## User Flow Changes + +### Before +1. App loads → Default file opened in editor +2. Bottom tab navigation between Editor/Files/Settings +3. Default file automatically created and managed + +### After +1. App loads → Files page (main landing page) +2. User selects existing file OR creates new file with template +3. Editor opens with selected file +4. Back buttons navigate to files page +5. Settings accessible from files page header + +## Navigation Pattern + +``` +FilesPage (Main) + ├── SettingsPage (accessible via header button, has back button) + └── Home/Editor (accessible when file selected, has back button) +``` + +## Benefits + +1. **Cleaner UX**: Users must explicitly choose files to work with +2. **No Hidden State**: No invisible "default" file confusing users +3. **File-Centric**: App revolves around saved files, encouraging better file management +4. **Simplified Navigation**: Linear navigation instead of tab-based +5. **Mobile-Friendly**: Back button navigation pattern familiar to mobile users + +## Technical Impact + +- **Reduced Complexity**: Removed default file logic throughout the app +- **Better File Management**: All files are explicitly named and saved +- **Cleaner State Management**: No special handling for "default" vs named files +- **Improved Error Handling**: Clear redirects when files don't exist + +## Testing Recommendations + +1. Verify files page loads as default route +2. Test template selection and file creation flow +3. Verify back button navigation from editor and settings +4. Test auto-save functionality with named files +5. Ensure settings button works from files page +6. Verify proper handling when accessing editor without selected file diff --git a/ARCHITECTURE_DIAGRAMS.md b/ARCHITECTURE_DIAGRAMS.md new file mode 100644 index 0000000..3a1dbab --- /dev/null +++ b/ARCHITECTURE_DIAGRAMS.md @@ -0,0 +1,266 @@ +# Multi-Template Architecture Diagram + +```mermaid +graph TB + subgraph "Application Layer" + App[App.tsx] + Pages[Pages Layer] + Components[Components Layer] + end + + subgraph "Template Management" + TI[TemplateInitializer] + TM[TemplateManager] + TD[templates.ts] + TMeta[templates-meta.ts] + end + + subgraph "Storage Layer" + LS[LocalStorage] + File[Enhanced File Class] + Preferences[Capacitor Preferences] + end + + subgraph "Template Data Structure" + T1[Template 1
Mobile Invoice 1] + T2[Template 2
Mobile Invoice 2] + TN[Template N
Custom Templates] + end + + subgraph "File Storage Strategy" + F1[template_1_file1.msc] + F2[template_1_file2.msc] + F3[template_2_file1.msc] + F4[template_2_file2.msc] + end + + subgraph "Metadata Structure" + Meta[TemplateMetadata] + Footers[Footers Array] + CellMap[Cell Mappings] + LogoCell[Logo Cell Reference] + SigCell[Signature Cell Reference] + end + + %% Initialization Flow + App -->|Initialize| TI + TI -->|Setup Default Metadata| TD + TI -->|Validate Templates| TD + TI -->|Create Registry| LS + + %% Template Management Flow + Pages -->|Template Operations| TM + TM -->|Extract Metadata| TD + TM -->|Filter Files| LS + + %% Storage Flow + Components -->|Save/Load Files| LS + LS -->|Enhanced File Creation| File + File -->|Store with Metadata| Preferences + + %% Template Isolation + T1 -->|Isolated Storage| F1 + T1 -->|Isolated Storage| F2 + T2 -->|Isolated Storage| F3 + T2 -->|Isolated Storage| F4 + + %% Metadata Flow + File -->|Contains| Meta + Meta -->|Includes| Footers + Meta -->|Includes| CellMap + Meta -->|Includes| LogoCell + Meta -->|Includes| SigCell + + %% Styling + classDef templateClass fill:#e1f5fe + classDef storageClass fill:#f3e5f5 + classDef metadataClass fill:#e8f5e8 + classDef fileClass fill:#fff3e0 + + class T1,T2,TN templateClass + class LS,File,Preferences storageClass + class Meta,Footers,CellMap,LogoCell,SigCell metadataClass + class F1,F2,F3,F4 fileClass +``` + +## Architecture Flow Diagram + +```mermaid +sequenceDiagram + participant User + participant App + participant TI as TemplateInitializer + participant TM as TemplateManager + participant LS as LocalStorage + participant Storage as Device Storage + + Note over User,Storage: Application Initialization + User->>App: Launch Application + App->>TI: Initialize Templates + TI->>TI: Validate Template Data + TI->>TI: Setup Default Metadata + TI->>LS: Create Template Registry + LS->>Storage: Store Registry + + Note over User,Storage: File Creation with Template + User->>App: Create New Invoice + App->>TM: Get Template Metadata + TM->>TI: Request Template Data + TI-->>TM: Return Template Metadata + TM-->>App: Enhanced File Creation + App->>LS: Save File with Metadata + LS->>Storage: Store File + Metadata + + Note over User,Storage: Template-Specific Operations + User->>App: Filter Files by Template + App->>LS: Get Files by Template ID + LS->>Storage: Query Template Files + Storage-->>LS: Return Filtered Files + LS-->>App: Template-Specific Files + App-->>User: Display Organized Files + + Note over User,Storage: Cross-Template Isolation + User->>App: Switch Template Type + App->>TM: Load Different Template + TM->>LS: Get Template Files + LS->>Storage: Isolated Storage Access + Storage-->>LS: Template-Isolated Data + LS-->>App: Clean Template Context + App-->>User: Isolated Template View +``` + +## Data Structure Diagram + +```mermaid +erDiagram + FILE { + string created + string modified + string name + string content + number billType + boolean isEncrypted + string password + TemplateMetadata templateMetadata + } + + TEMPLATE_METADATA { + string template + number templateId + Footer[] footers + string logoCell + string signatureCell + CellMappings cellMappings + } + + FOOTER { + string name + number index + boolean isActive + } + + CELL_MAPPINGS { + string headingName + CellDefinition[] cells + } + + CELL_DEFINITION { + string cellName + string heading + string datatype + } + + TEMPLATE_DATA { + string template + number templateId + MSC_DATA msc + Footer[] footers + string logoCell + string signatureCell + CellMappings cellMappings + } + + MSC_DATA { + number numsheets + string currentid + string currentname + SheetArray sheetArr + EditableCells EditableCells + } + + EDITABLE_CELLS { + boolean allow + CellReference[] cells + Constraints[] constraints + } + + FILE ||--|| TEMPLATE_METADATA : contains + TEMPLATE_METADATA ||--o{ FOOTER : has + TEMPLATE_METADATA ||--|| CELL_MAPPINGS : defines + CELL_MAPPINGS ||--o{ CELL_DEFINITION : contains + TEMPLATE_DATA ||--|| MSC_DATA : includes + TEMPLATE_DATA ||--o{ FOOTER : defines + MSC_DATA ||--|| EDITABLE_CELLS : configures +``` + +## Storage Isolation Diagram + +```mermaid +graph LR + subgraph "Template 1 Files" + T1F1[template_1_invoice1.msc] + T1F2[template_1_invoice2.msc] + T1F3[template_1_draft1.msc] + end + + subgraph "Template 2 Files" + T2F1[template_2_receipt1.msc] + T2F2[template_2_receipt2.msc] + T2F3[template_2_quote1.msc] + end + + subgraph "Template N Files" + TNF1[template_n_custom1.msc] + TNF2[template_n_custom2.msc] + end + + subgraph "Storage Operations" + Filter[Filter by Template] + Isolate[Template Isolation] + Organize[File Organization] + end + + subgraph "Benefits" + NoInterference[No Cross-Template Interference] + EasyManagement[Easy File Management] + ClearSeparation[Clear Separation of Concerns] + end + + T1F1 --> Filter + T1F2 --> Filter + T1F3 --> Filter + T2F1 --> Filter + T2F2 --> Filter + T2F3 --> Filter + TNF1 --> Filter + TNF2 --> Filter + + Filter --> Isolate + Isolate --> Organize + Organize --> NoInterference + Organize --> EasyManagement + Organize --> ClearSeparation + + %% Styling + classDef template1 fill:#ffebee + classDef template2 fill:#e8f5e8 + classDef templateN fill:#e1f5fe + classDef operation fill:#fff3e0 + classDef benefit fill:#f3e5f5 + + class T1F1,T1F2,T1F3 template1 + class T2F1,T2F2,T2F3 template2 + class TNF1,TNF2 templateN + class Filter,Isolate,Organize operation + class NoInterference,EasyManagement,ClearSeparation benefit +``` diff --git a/FEATURE_IMPLEMENTATION_SUMMARY.md b/FEATURE_IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..3219f62 --- /dev/null +++ b/FEATURE_IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,89 @@ +# Multi-Template Feature Implementation Summary + +## Overview +Successfully implemented multi-template selection and metadata-aware autosave functionality in the Government Invoice Form application. + +## Key Features Implemented + +### 1. Template Selection in Files Component +- **Template Filtering**: Added dropdown to filter files by template type +- **Template Display**: Shows template information for each file +- **New File Creation**: Modal for creating new files with template selection +- **Visual Indicators**: Clear template identification in file listings + +### 2. Metadata-Aware Autosave in Home Page +- **Template Metadata**: All files now save with template metadata +- **Backward Compatibility**: Existing files without metadata are handled gracefully +- **Auto-Detection**: Template metadata is automatically extracted during save operations +- **Template Context**: Active template is preserved in file metadata + +### 3. Enhanced File Management +- **Template-Aware Storage**: Files are stored with comprehensive template metadata +- **Migration Ready**: System can identify and migrate legacy files +- **Data Integrity**: Template metadata includes all necessary template information + +## Technical Implementation + +### Files Component Updates (`src/components/Files/Files.tsx`) +```typescript +// Key additions: +- Template filtering state and UI +- Template selection in new file modal +- Template information display +- Template-aware file operations +``` + +### Home Page Updates (`src/pages/Home.tsx`) +```typescript +// Key additions: +- TemplateInitializer integration +- Metadata extraction during save +- Template-aware initialization +- Enhanced error handling +``` + +### Supporting Architecture +- **TemplateManager**: Utility for template operations +- **TemplateInitializer**: App initialization and metadata management +- **Enhanced File Class**: Template metadata support with backward compatibility + +## User Experience Improvements + +1. **Template Selection**: Users can easily filter and view files by template +2. **New File Creation**: Guided template selection when creating new files +3. **Template Awareness**: Clear indication of which template each file uses +4. **Seamless Migration**: Existing files continue to work without interruption + +## Data Structure +```typescript +interface TemplateMetadata { + template: string; + templateId: string; + footers: string[]; + logoCell: string | null; + signatureCell: string | null; + cellMappings: Record; +} +``` + +## Testing Recommendations + +1. **Template Filtering**: Verify filtering works correctly across all templates +2. **New File Creation**: Test file creation with different template selections +3. **Autosave**: Confirm template metadata is preserved during autosave +4. **Legacy Files**: Ensure existing files without metadata continue to function +5. **Template Switching**: Test switching between templates in the editor + +## Next Steps + +1. **Performance Testing**: Monitor performance with large numbers of files +2. **User Feedback**: Gather feedback on template selection UX +3. **Migration Utility**: Implement automatic migration for legacy files +4. **Template Management**: Consider adding template management features + +## Architecture Benefits + +- **Scalability**: Easy to add new templates without affecting existing ones +- **Maintainability**: Clear separation of template logic and file management +- **User Experience**: Intuitive template selection and file organization +- **Data Integrity**: Comprehensive metadata ensures template context is preserved diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..4a71238 --- /dev/null +++ b/IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,323 @@ +# Multi-Template Architecture Implementation + +## 🎯 Overview + +This implementation introduces a comprehensive multi-template architecture for the Government Invoice Form application. The new system isolates MSC files by template type, preventing interference between different templates while maintaining full backward compatibility. + +## 🚀 Key Features + +### ✅ Template Isolation +- Each MSC file is associated with specific template metadata +- No cross-template interference +- Template-specific configurations are preserved +- Clean separation of concerns + +### ✅ Enhanced Metadata Management +- Rich template metadata structure +- Footer management per template +- Logo and signature cell references +- Flexible cell mapping system + +### ✅ Backward Compatibility +- Existing files continue to work without modification +- Gradual migration path +- No breaking changes to existing functionality +- Enhanced constructor supports both old and new signatures + +### ✅ Improved Organization +- Files organized by template type +- Easy filtering and management +- Template-specific operations +- Clear file categorization + +## 📁 New File Structure + +``` +src/ +├── components/ +│ ├── Storage/ +│ │ └── LocalStorage.ts # Enhanced with template metadata +│ └── TemplateFiles.tsx # Example multi-template component +├── utils/ +│ ├── templateManager.ts # Template utility functions +│ └── templateInitializer.ts # App initialization with templates +├── templates.ts # Enhanced template definitions +├── templates-meta.ts # Template metadata (existing) +└── App.tsx # Updated with template initialization +``` + +## 🔧 Implementation Details + +### Enhanced File Class + +The `File` class now includes template metadata while maintaining backward compatibility: + +```typescript +// NEW: With template metadata +const file = new File( + created, modified, content, name, billType, + templateMetadata, // TemplateMetadata object + isEncrypted, password +); + +// OLD: Still works (backward compatible) +const file = new File( + created, modified, content, name, billType, + isEncrypted, password +); +``` + +### Template Metadata Structure + +```typescript +interface TemplateMetadata { + template: string; // Template name + templateId: number; // Unique identifier + footers: Array<{ // Template-specific footers + name: string; + index: number; + isActive: boolean; + }>; + logoCell: string | null; // Logo cell reference + signatureCell: string | null; // Signature cell reference + cellMappings: { // Template-specific cell mappings + [headingName: string]: { + [cellName: string]: { + heading: string; + datatype: string; + }; + }; + }; +} +``` + +### Storage Strategy + +Files are now stored with template-specific keys: +``` +template_{templateId}_{fileName} +``` + +Examples: +- `template_1_invoice_001.msc` +- `template_2_receipt_001.msc` + +## 🎨 Usage Examples + +### 1. Initialize Template System + +```typescript +import { TemplateInitializer } from './utils/templateInitializer'; + +// App initialization +await TemplateInitializer.initializeApp(); +``` + +### 2. Create Template-Aware Files + +```typescript +import { TemplateInitializer } from './utils/templateInitializer'; +import { File } from './components/Storage/LocalStorage'; + +// Get template metadata +const metadata = TemplateInitializer.getTemplateMetadata(1); + +// Create new file with template +const file = new File( + new Date().toISOString(), + new Date().toISOString(), + mscContent, + "invoice.msc", + 1, + metadata +); +``` + +### 3. Filter Files by Template + +```typescript +import { TemplateManager } from './utils/templateManager'; + +// Get files for specific template +const template1Files = await local._getFilesByTemplate(1); + +// Filter existing files collection +const filteredFiles = TemplateManager.filterFilesByTemplate(allFiles, 1); +``` + +### 4. Work with Cell Mappings + +```typescript +// Generate default mappings +const defaultMappings = TemplateManager.generateDefaultCellMappings(1); + +// Merge additional mappings +const mergedMappings = TemplateManager.mergeCellMappings( + existingMappings, + additionalMappings +); +``` + +## 🔄 Migration Path + +### Phase 1: Current Implementation +- ✅ New architecture implemented +- ✅ Backward compatibility maintained +- ✅ Enhanced metadata structure +- ✅ Template isolation functionality + +### Phase 2: Gradual Enhancement (Future) +- Migrate existing files to new structure +- Enhanced UI for template management +- Advanced template customization + +### Phase 3: Full Optimization (Future) +- Complete transition to new architecture +- Performance optimizations +- Advanced template features + +## 🛠 Developer Guide + +### Adding New Templates + +1. **Define Template Data** (`src/templates.ts`): +```typescript +export let DATA = { + // ... existing templates + 3: { + template: "New Template Type", + templateId: 3, + msc: { /* MSC configuration */ }, + footers: [ + { name: "New Footer", index: 1, isActive: true } + ], + logoCell: null, + signatureCell: null, + cellMappings: { /* cell mappings */ } + } +}; +``` + +2. **Update Template Metadata** (`src/templates-meta.ts`): +```typescript +export let tempMeta = [ + // ... existing metadata + { + name: "New Template Type", + template_id: 3, + ImageUri: "base64_image_string" + } +]; +``` + +3. **Test Template Isolation**: +```typescript +// Verify new template works in isolation +const template3Files = await local._getFilesByTemplate(3); +``` + +### Extending Metadata + +1. **Update Interface**: +```typescript +interface TemplateMetadata { + // ... existing properties + newProperty: string; // Add new property +} +``` + +2. **Update Validation**: +```typescript +// In TemplateManager.validateMetadata() +return ( + // ... existing validations + typeof metadata.newProperty === 'string' +); +``` + +3. **Update Default Generation**: +```typescript +// In TemplateManager.generateDefaultCellMappings() +// Add handling for new property +``` + +## 📊 Benefits Achieved + +### 🎯 Template Isolation +- ✅ No interference between different template types +- ✅ Independent template configurations +- ✅ Clean separation of template-specific data + +### 📈 Improved Organization +- ✅ Files categorized by template +- ✅ Easy filtering and management +- ✅ Template-specific operations + +### 🔧 Enhanced Extensibility +- ✅ Easy addition of new templates +- ✅ Flexible metadata structure +- ✅ Template-specific customizations + +### 🔄 Backward Compatibility +- ✅ Existing files continue to work +- ✅ No breaking changes +- ✅ Gradual migration path + +### 💾 Robust Storage +- ✅ Self-contained file metadata +- ✅ No external dependencies +- ✅ Easy backup and restore + +## 🧪 Testing the Implementation + +### 1. Template Isolation Test +```typescript +// Create files with different templates +const file1 = new File(/* template 1 data */); +const file2 = new File(/* template 2 data */); + +// Verify isolation +const template1Files = await local._getFilesByTemplate(1); +const template2Files = await local._getFilesByTemplate(2); + +// Should only contain respective template files +assert(template1Files contains only template 1 files); +assert(template2Files contains only template 2 files); +``` + +### 2. Backward Compatibility Test +```typescript +// Old constructor should still work +const oldStyleFile = new File( + created, modified, content, name, billType, isEncrypted, password +); + +// Should have default metadata +assert(oldStyleFile.templateMetadata exists); +assert(oldStyleFile.templateMetadata.templateId === billType); +``` + +### 3. Metadata Validation Test +```typescript +const metadata = TemplateInitializer.getTemplateMetadata(1); +const isValid = TemplateManager.validateMetadata(metadata); +assert(isValid === true); +``` + +## 📚 Documentation + +- **[MULTI_TEMPLATE_ARCHITECTURE.md](./MULTI_TEMPLATE_ARCHITECTURE.md)** - Comprehensive architecture documentation +- **[ARCHITECTURE_DIAGRAMS.md](./ARCHITECTURE_DIAGRAMS.md)** - Visual diagrams and flowcharts +- **[TemplateFiles.tsx](./src/components/TemplateFiles.tsx)** - Example implementation component + +## 🎉 Conclusion + +The multi-template architecture successfully provides: + +1. **Complete template isolation** - Different MSC files no longer interfere with each other +2. **Enhanced organization** - Files are properly categorized and manageable by template +3. **Backward compatibility** - All existing functionality continues to work seamlessly +4. **Extensible design** - Easy to add new templates and enhance functionality +5. **Robust metadata system** - Rich template information stored with each file + +This implementation creates a solid foundation for scalable invoice template management while maintaining the reliability and functionality of the existing system. diff --git a/IONALERT_IMPLEMENTATION.md b/IONALERT_IMPLEMENTATION.md new file mode 100644 index 0000000..6b74bc7 --- /dev/null +++ b/IONALERT_IMPLEMENTATION.md @@ -0,0 +1,115 @@ +# IonAlert Implementation for File Creation - Summary + +## Change Made + +Successfully updated the FilesPage.tsx to use an IonAlert for file creation instead of an IonModal, making it consistent with the rename file functionality in Files.tsx. + +## Before vs After + +### Before (IonModal) +```tsx + + + + Create New File + + + + + + +
+
+

Template Info

+

Template Details

+
+
+ File Name + +
+
+ Cancel + Create +
+
+
+
+``` + +### After (IonAlert) +```tsx + { + // Handle file creation + }, + }, + ]} +/> +``` + +## Benefits of Using IonAlert + +1. **Consistency**: Now matches the exact style and behavior of the rename file alert +2. **Simplicity**: Much cleaner code with less boilerplate +3. **Native Feel**: IonAlert provides a more native mobile experience +4. **Smaller Bundle**: Removed unused modal-related imports and components +5. **Better UX**: Simpler, more focused interaction for users + +## Technical Changes + +### Removed Imports +- `IonCard`, `IonCardContent`, `IonCardHeader`, `IonCardTitle`, `IonCardSubtitle` +- `IonModal`, `IonInput`, `IonLabel` +- `add`, `close` icons + +### Kept Imports +- `IonAlert` for the new implementation +- Template-related icons (`chevronForward`, `chevronUp`, `chevronDown`, `layers`) + +### Alert Configuration +- **Header**: "Create New File" +- **Message**: Dynamic message based on selected template +- **Input**: Single text input for filename +- **Buttons**: Cancel and Create with proper handlers + +## User Experience + +The new implementation provides: +- Immediate focus on filename input +- Clear template context in the message +- Consistent button layout (Cancel | Create) +- Same look and feel as rename functionality +- Faster interaction with less UI complexity + +## Code Reduction + +Reduced the component from: +- ~50 lines of modal JSX +- Multiple component imports +- Complex layout management + +To: +- ~30 lines of alert configuration +- Minimal imports +- Simple, declarative structure + +This change makes the file creation flow more consistent with the existing rename functionality and provides a cleaner, more native user experience. diff --git a/MULTI_TEMPLATE_ARCHITECTURE.md b/MULTI_TEMPLATE_ARCHITECTURE.md new file mode 100644 index 0000000..9e525d1 --- /dev/null +++ b/MULTI_TEMPLATE_ARCHITECTURE.md @@ -0,0 +1,289 @@ +# Multi-Template Architecture Documentation + +## Overview + +The Government Invoice Form application has been enhanced with a new multi-template architecture that isolates different MSC files and their metadata. This prevents interference between different template types and provides better organization and extensibility. + +## Architecture Changes + +### 1. Enhanced File Structure + +The new architecture introduces template-specific metadata and isolation: + +```typescript +interface TemplateMetadata { + template: string; // Template name + templateId: number; // Unique template identifier + footers: Array<{ // Template-specific footers + name: string; + index: number; + isActive: boolean; + }>; + logoCell: string | null; // Cell reference for logo + signatureCell: string | null; // Cell reference for signature + cellMappings: { // Template-specific cell mappings + [headingName: string]: { + [cellName: string]: { + heading: string; + datatype: string; + }; + }; + }; +} +``` + +### 2. Enhanced LocalStorage + +The `File` class now includes template metadata: + +```typescript +export class File { + created: string; + modified: string; + name: string; + content: string; // MSC content + billType: number; // Template ID (for backward compatibility) + isEncrypted: boolean; + password?: string; + templateMetadata: TemplateMetadata; // NEW: Template-specific metadata +} +``` + +**Backward Compatibility**: The constructor supports both old and new signatures to ensure existing code continues to work. + +### 3. Template Management System + +#### TemplateManager Utility (`/src/utils/templateManager.ts`) + +Provides utilities for: +- Extracting metadata from template data +- Template-specific storage key generation +- Metadata validation +- Cell mapping operations +- File filtering by template + +#### TemplateInitializer (`/src/utils/templateInitializer.ts`) + +Handles: +- Application initialization with multi-template support +- Template data validation +- Default metadata setup +- Template registry management + +## Key Benefits + +### 1. Template Isolation +- Each MSC file is associated with specific template metadata +- No cross-template interference +- Template-specific configurations are preserved + +### 2. Enhanced Organization +- Files are organized by template type +- Easy filtering and management by template +- Clear separation of concerns + +### 3. Extensibility +- Easy addition of new templates +- Template-specific customizations +- Flexible metadata structure + +### 4. Backward Compatibility +- Existing files continue to work +- Gradual migration path +- No breaking changes to existing API + +## Template Data Structure + +Templates are defined in `/src/templates.ts` with the following structure: + +```typescript +export interface TemplateData { + template: string; // Display name + templateId: number; // Unique identifier + msc: { // MSC spreadsheet data + numsheets: number; + currentid: string; + currentname: string; + sheetArr: { ... }; // Sheet definitions + EditableCells: { // Editable cell configuration + allow: boolean; + cells: { ... }; + constraints: { ... }; // Cell validation rules + }; + }; + footers: Array<{ // Template footers + name: string; + index: number; + isActive: boolean; + }>; + logoCell: string | null; + signatureCell: string | null; + cellMappings: { ... }; // Cell mapping definitions +} +``` + +## Usage Examples + +### 1. Creating a New File with Template Metadata + +```typescript +import { File, TemplateMetadata } from './components/Storage/LocalStorage'; +import { TemplateInitializer } from './utils/templateInitializer'; + +// Get template metadata +const metadata = TemplateInitializer.getTemplateMetadata(1); + +// Create new file +const file = new File( + new Date().toISOString(), // created + new Date().toISOString(), // modified + mscContent, // MSC content + "my-invoice.msc", // filename + 1, // template ID + metadata, // template metadata + false // not encrypted +); +``` + +### 2. Filtering Files by Template + +```typescript +import { TemplateManager } from './utils/templateManager'; + +// Get all files for template ID 1 +const template1Files = await local._getFilesByTemplate(1); + +// Or filter existing files collection +const filteredFiles = TemplateManager.filterFilesByTemplate(allFiles, 1); +``` + +### 3. Working with Cell Mappings + +```typescript +import { TemplateManager } from './utils/templateManager'; + +// Get default cell mappings for a template +const defaultMappings = TemplateManager.generateDefaultCellMappings(1); + +// Merge additional mappings +const mergedMappings = TemplateManager.mergeCellMappings( + existingMappings, + additionalMappings +); +``` + +## File Storage Strategy + +### Storage Key Format +Files are stored with template-specific keys: +``` +template_{templateId}_{fileName} +``` + +Example: `template_1_invoice_001.msc` + +### Metadata Storage +Each file stores complete template metadata, ensuring: +- Self-contained file information +- No external dependencies +- Easy backup and restore +- Cross-device compatibility + +## Migration Strategy + +### Phase 1: Backward Compatibility (Current) +- New architecture runs alongside existing system +- Existing files continue to work without modification +- New files use enhanced metadata structure + +### Phase 2: Gradual Migration (Future) +- Utility to migrate existing files to new structure +- Optional metadata enhancement for old files +- Preservation of all existing functionality + +### Phase 3: Full Migration (Future) +- Complete transition to new architecture +- Cleanup of legacy code paths +- Performance optimizations + +## Template Registry + +The application maintains a template registry in localStorage: + +```json +{ + "version": "2.0.0", + "templates": [ + { + "id": 1, + "name": "Mobile Invoice 1", + "version": "1.0.0", + "created": "2025-01-XX", + "modified": "2025-01-XX" + } + ], + "initialized": "2025-01-XX" +} +``` + +## Error Handling + +The system includes comprehensive error handling: +- Template validation on initialization +- Graceful fallbacks for missing metadata +- Error logging and recovery mechanisms +- User-friendly error messages + +## Development Guidelines + +### Adding New Templates +1. Define template data in `/src/templates.ts` +2. Assign unique `templateId` +3. Define appropriate cell mappings +4. Test template isolation + +### Extending Metadata +1. Update `TemplateMetadata` interface +2. Update `File` class if needed +3. Add validation logic +4. Update documentation + +### Testing Template Isolation +1. Create files with different templates +2. Verify no cross-template interference +3. Test filtering and organization +4. Validate metadata integrity + +## Performance Considerations + +- Template metadata is stored with each file (slight storage overhead) +- Benefits outweigh costs due to improved organization +- No additional network requests required +- Local storage remains primary storage mechanism + +## Security Considerations + +- Template metadata is stored in plaintext (non-sensitive data) +- Encryption applies only to MSC content when enabled +- Template isolation prevents accidental data mixing +- No new security vectors introduced + +## Future Enhancements + +### Planned Features +1. Template versioning system +2. Template import/export functionality +3. Custom template creation tools +4. Advanced cell mapping editor +5. Template sharing capabilities + +### Potential Improvements +1. Template validation UI +2. Migration assistant tool +3. Template performance analytics +4. Bulk template operations +5. Template backup/restore tools + +--- + +This multi-template architecture provides a solid foundation for scalable invoice template management while maintaining full backward compatibility with existing functionality. diff --git a/TEMPLATE_MIGRATION_SUMMARY.md b/TEMPLATE_MIGRATION_SUMMARY.md new file mode 100644 index 0000000..37cfa12 --- /dev/null +++ b/TEMPLATE_MIGRATION_SUMMARY.md @@ -0,0 +1,154 @@ +# Template Selection UI Migration - Implementation Summary + +## Overview +Successfully moved the template selection UI from the Files component to the FilesPage component, replacing the original "Create New Invoice" and "Medication Invoice" buttons with a more comprehensive template selection system. + +## Key Changes Made + +### 1. FilesPage.tsx - Enhanced Template Section + +#### Added Imports +- Added Ionic components: `IonButton`, `IonIcon`, `IonCard`, `IonCardContent`, etc. +- Added icons: `add`, `close`, `chevronForward`, `chevronUp`, `chevronDown`, `layers` +- Added template utilities: `tempMeta`, `TemplateInitializer` + +#### New State Management +```typescript +const [showAllTemplates, setShowAllTemplates] = useState(false); +const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); +const [selectedTemplateForFile, setSelectedTemplateForFile] = useState(null); +const [newFileName, setNewFileName] = useState(""); +``` + +#### Template Helper Functions +- `getAvailableTemplates()` - Gets all available templates +- `getTemplateInfo(templateId)` - Gets template display name +- `getTemplateMetadata(templateId)` - Gets template metadata from tempMeta +- `handleTemplateSelect(templateId)` - Handles template selection +- `createNewFileWithTemplate(templateId, fileName)` - Creates new file with selected template + +#### Enhanced Template Cards Display +- **Grid Layout**: Responsive grid with 280px minimum column width +- **Visual Template Cards**: Each card shows: + - Template image (80x80px) from base64 ImageUri + - Template name from metadata + - Footer count + - Template ID + - Forward arrow icon +- **Interactive Effects**: Hover animations with border color change and elevation +- **Progressive Disclosure**: Shows first 3 templates, expandable to show all +- **Expand/Collapse Button**: Smart button showing count of additional templates + +#### File Creation Flow +1. User clicks template card +2. Filename prompt modal appears with template context +3. User enters filename and clicks "Create File" +4. File created with template metadata and user navigated to editor + +### 2. Files.tsx - Simplified Component + +#### Removed Elements +- Template creation section with cards +- Filename prompt modal +- Template selection functions +- Unused state variables and imports + +#### Retained Elements +- File listing and management functionality +- Template filtering for existing files +- Search and sort capabilities +- File operations (edit, delete, rename) + +### 3. Template Integration Features + +#### Template Metadata Support +- Uses `templates-meta.ts` for template images and names +- Integrates with `TemplateInitializer` for template management +- Proper mapping between template IDs and metadata + +#### Enhanced User Experience +- **Visual Template Selection**: Images provide better template identification +- **Context-Aware Creation**: Shows selected template in filename prompt +- **Seamless Navigation**: Direct navigation to editor after file creation +- **Template Information**: Clear display of template details + +## User Experience Improvements + +### Before +- Two fixed buttons: "Create New Invoice" and "Medication Invoice" +- Limited template options +- Generic file creation process +- No visual template identification + +### After +- Dynamic template cards showing all available templates +- Visual template preview with images +- Template-specific information display +- Context-aware file creation +- Expandable template list for better organization + +## Technical Implementation Details + +### Template Card Structure +```tsx +
handleTemplateSelect(template.templateId)}> + {/* Template Image (80x80px) */} +
+ +
+ + {/* Template Information */} +
+

{metadata.name}

+

{template.footers.length} footer(s)

+

Template ID: {template.templateId}

+
+ + {/* Navigation Icon */} + +
+``` + +### Filename Prompt Modal +- Context-aware title showing selected template +- Template name in subtitle +- Input validation +- Cancel and create actions +- Automatic cleanup on completion + +### Progressive Disclosure +- Shows first 3 templates by default +- "View X More Templates" button when applicable +- "Show Less" option when expanded +- Maintains clean interface while providing access to all templates + +## Benefits Achieved + +1. **Better Template Discovery**: All templates are visible and accessible +2. **Visual Template Identification**: Images help users identify templates quickly +3. **Scalable Design**: Easy to add new templates without UI changes +4. **Cleaner Architecture**: Template creation logic centralized in FilesPage +5. **Enhanced User Flow**: More intuitive template selection process +6. **Mobile-Friendly**: Responsive design works well on all screen sizes + +## File Structure Impact + +### Modified Files +- `src/pages/FilesPage.tsx` - Enhanced with template selection UI +- `src/components/Files/Files.tsx` - Simplified, focused on file management + +### Dependencies Used +- `src/templates-meta.ts` - Template metadata and images +- `src/utils/templateInitializer.ts` - Template management utilities +- Ionic React components for UI elements + +## Testing Recommendations + +1. **Template Display**: Verify all templates show with correct images and information +2. **File Creation**: Test complete flow from template selection to file creation +3. **Progressive Disclosure**: Test expand/collapse functionality +4. **Responsive Design**: Verify layout on different screen sizes +5. **Error Handling**: Test with invalid template data or missing metadata +6. **Navigation**: Ensure proper navigation to editor after file creation + +This migration successfully transforms the template selection experience from a static button-based approach to a dynamic, visual, and scalable template selection system that better serves user needs and provides a foundation for future template additions. diff --git a/TEMPLATE_UI_IMPROVEMENTS.md b/TEMPLATE_UI_IMPROVEMENTS.md new file mode 100644 index 0000000..095108c --- /dev/null +++ b/TEMPLATE_UI_IMPROVEMENTS.md @@ -0,0 +1,88 @@ +# Template UI Improvements - Summary + +## Changes Made + +### 1. Removed Template ID from Template Cards +**Before:** +- Template cards showed "Template ID: X" +- Extra line of text cluttering the interface + +**After:** +- Cleaner template cards with just template name and footer count +- More professional and user-friendly appearance + +### 2. Simplified Create New File Modal + +**Before:** +- Complex modal with IonCard structure +- IonCardHeader, IonCardTitle, IonCardSubtitle components +- Bulky appearance with extra padding and structure +- "Create File" button with icon + +**After:** +- Simple, clean modal similar to rename file modal +- Direct content layout without card wrapper +- Centered title and subtitle information +- Streamlined button layout (Cancel | Create) +- Consistent with existing rename file modal design + +## Visual Improvements + +### Template Cards +``` +Old Layout: +┌─────────────────────────┐ +│ [IMG] Template Name │ +│ X footer(s) │ +│ Template ID: X │ ← Removed +└─────────────────────────┘ + +New Layout: +┌─────────────────────────┐ +│ [IMG] Template Name │ +│ X footer(s) │ +└─────────────────────────┘ +``` + +### Modal Layout +``` +Old Modal: +┌─────────────────────────┐ +│ Create New File [×] │ +├─────────────────────────┤ +│ ┌─────────────────────┐ │ +│ │ Card Header │ │ +│ │ ├─────────────────┤ │ │ +│ │ │ Card Content │ │ │ +│ │ │ Input Field │ │ │ +│ │ │ [Cancel][Create]│ │ │ +│ │ └─────────────────┘ │ │ +│ └─────────────────────┘ │ +└─────────────────────────┘ + +New Modal: +┌─────────────────────────┐ +│ Create New File [×] │ +├─────────────────────────┤ +│ Template Info │ +│ Input Field │ +│ [Cancel] [Create] │ +└─────────────────────────┘ +``` + +## Benefits + +1. **Cleaner Interface**: Removed unnecessary template ID reduces visual clutter +2. **Better Consistency**: Modal now matches the style of rename file modal +3. **Improved UX**: Simpler modal is easier to understand and interact with +4. **Professional Look**: Cleaner template cards look more polished +5. **Focus on Essentials**: Users see only the information they need (name, footer count) + +## User Impact + +- **Template Selection**: Users can focus on template name and functionality rather than technical IDs +- **File Creation**: Simplified modal reduces cognitive load during file creation +- **Visual Harmony**: Consistent modal design across the application +- **Mobile Friendly**: Simpler layout works better on smaller screens + +The changes maintain all functionality while providing a cleaner, more professional user interface that aligns with modern design principles. diff --git a/TEMPLATE_UI_REDESIGN_SUMMARY.md b/TEMPLATE_UI_REDESIGN_SUMMARY.md new file mode 100644 index 0000000..44e41f6 --- /dev/null +++ b/TEMPLATE_UI_REDESIGN_SUMMARY.md @@ -0,0 +1,120 @@ +# Template Selection UI Redesign - Implementation Summary + +## Overview +Successfully redesigned the Files component to replace the "Create New Invoice" and "Medication Invoice" buttons with direct template selection cards, creating a more intuitive and visual template selection experience. + +## Key Changes Implemented + +### 1. Template Cards Display +- **Direct Template Access**: Removed modal-based template selection in favor of immediate template cards display +- **Visual Template Cards**: Each template shows: + - Template image (from ImageUri in templates-meta.ts) + - Template name + - Footer count + - Template ID for mapping + - Interactive hover effects + +### 2. Smart Template Layout +- **Grid Layout**: Responsive grid showing up to 3 templates initially +- **Expand/Collapse**: "View More Templates" button for additional templates +- **Progressive Disclosure**: Cleaner interface showing most important templates first + +### 3. Streamlined File Creation Flow +- **One-Click Template Selection**: Clicking a template immediately starts file creation +- **Simple Filename Prompt**: Modal appears only for filename input +- **Context-Aware**: Shows selected template info in the filename prompt + +### 4. Enhanced Template Metadata Integration +- **Template Metadata**: Utilizes `templates-meta.ts` for template images and names +- **Image Display**: Properly renders template images from base64 ImageUri +- **Fallback Icons**: Shows layer icon when template image is not available + +## Technical Implementation Details + +### New State Variables +```typescript +const [showAllTemplates, setShowAllTemplates] = useState(false); +const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); +const [selectedTemplateForFile, setSelectedTemplateForFile] = useState(null); +``` + +### Helper Functions Added +```typescript +const getTemplateMetadata = (templateId: number) => { + return tempMeta.find(meta => meta.template_id === templateId); +}; + +const handleTemplateSelect = (templateId: number) => { + setSelectedTemplateForFile(templateId); + setNewFileTemplate(templateId); + setShowFileNamePrompt(true); +}; +``` + +### UI Components Structure +1. **Template Cards Section**: Grid layout with template information +2. **Expand/Collapse Control**: Smart button for additional templates +3. **Filename Prompt Modal**: Simplified modal for file naming +4. **Template Information Display**: Shows selected template context + +## User Experience Improvements + +### Before +- Multiple button clicks required (Create New → Select Template → Enter Name) +- Template selection hidden in modal +- Less visual template identification + +### After +- Single click to select template +- Visual template cards with images +- Immediate template information visibility +- Streamlined file creation process + +## Visual Design Features + +### Template Cards +- **Fixed Size Images**: 60x60px template preview images +- **Hover Effects**: Interactive feedback with border color changes +- **Information Display**: Template name, footer count, and ID +- **Responsive Grid**: Adapts to screen size + +### Smart Controls +- **Progressive Disclosure**: Show/hide additional templates +- **Clear Visual Hierarchy**: Template cards → Expand button → File list +- **Consistent Styling**: Matches existing design system + +## File Structure Updates + +### Modified Files +- `src/components/Files/Files.tsx` - Main implementation +- Added import for `templates-meta.ts` for template metadata + +### New Dependencies +- Utilizes existing `tempMeta` array for template images and names +- Integrates with existing `TemplateInitializer` for template management + +## Testing Recommendations + +1. **Template Display**: Verify all templates show with correct images and metadata +2. **File Creation Flow**: Test complete flow from template selection to file creation +3. **Responsive Behavior**: Check layout on different screen sizes +4. **Expand/Collapse**: Verify show more/less functionality works correctly +5. **Error Handling**: Test with missing template metadata or images + +## Future Enhancement Opportunities + +1. **Template Previews**: Add larger preview images or template previews +2. **Template Categories**: Group templates by type or purpose +3. **Recent Templates**: Show frequently used templates first +4. **Template Search**: Add search functionality for templates +5. **Template Management**: Allow users to customize or organize templates + +## Benefits Achieved + +- **Improved Discoverability**: Templates are immediately visible +- **Faster Workflow**: Reduced clicks for file creation +- **Better Visual Design**: Template cards provide better context +- **Scalable Architecture**: Easy to add more templates without cluttering UI +- **Mobile-Friendly**: Responsive design works well on mobile devices + +This redesign successfully transforms the template selection experience from a hidden, multi-step process to an intuitive, visual, and efficient workflow that better serves user needs. diff --git a/URL_FILE_EDITING.md b/URL_FILE_EDITING.md new file mode 100644 index 0000000..48cf2e6 --- /dev/null +++ b/URL_FILE_EDITING.md @@ -0,0 +1,116 @@ +# URL-Based File Editing System + +## Summary +Implemented a dedicated URL structure for editing specific files with proper file existence validation and user-friendly error handling. + +## New URL Structure + +### Routes +- `/app/files` - File explorer (main landing page) +- `/app/editor` - Editor without specific file (redirects to files) +- `/app/editor/:fileName` - Editor with specific file +- `/app/settings` - Settings page + +### Examples +- `/app/editor/invoice-2024-01` - Edit file named "invoice-2024-01" +- `/app/editor/My%20Invoice` - Edit file named "My Invoice" (URL encoded) + +## Implementation Details + +### 1. App.tsx - Updated Routing +```tsx + + + + + + +``` + +### 2. Home.tsx - File Parameter Handling +- **URL Parameter Extraction**: Uses `useParams<{ fileName?: string }>()` to get filename from URL +- **File Existence Check**: Validates if the requested file exists in local storage +- **Context Synchronization**: Updates the invoice context if URL parameter differs from selected file +- **Error State**: Shows "File Not Found" UI when file doesn't exist + +### 3. FilesPage.tsx - Updated Navigation +- **File Creation**: Redirects to `/app/editor/${fileName}` after creating new files +- **URL Encoding**: Uses `encodeURIComponent()` for filenames with special characters + +### 4. Files.tsx - Updated File Opening +- **File Opening**: Navigates to `/app/editor/${fileName}` when opening existing files +- **URL Encoding**: Handles filenames with spaces and special characters + +## User Experience + +### File Not Found State +When accessing a non-existent file via URL: +- Shows a clean error message +- Displays file icon and "File Not Found" heading +- Provides clear explanation +- Shows "Go to File Explorer" button to redirect to `/app/files` + +### Navigation Flow +1. **Files Page**: User selects or creates a file +2. **URL Navigation**: App navigates to `/app/editor/{fileName}` +3. **File Loading**: Home component loads the specific file +4. **Error Handling**: If file doesn't exist, shows error with option to return to files + +## Benefits + +### 1. Direct File Access +- Users can bookmark specific files +- Share direct links to files +- Browser back/forward works correctly + +### 2. Better Error Handling +- Clear feedback when files don't exist +- Graceful fallback to file explorer +- No confusing redirects + +### 3. URL Consistency +- Predictable URL patterns +- RESTful-style resource access +- Better browser integration + +### 4. File Management +- URL reflects current file being edited +- Easy to see which file is active +- Better browser history + +## Technical Features + +### URL Encoding +- Handles filenames with spaces: `My File` → `My%20File` +- Supports special characters safely +- Decodes properly in component + +### State Management +- Synchronizes URL parameters with React context +- Updates selected file when URL changes +- Maintains consistency between URL and app state + +### Error Boundaries +- Validates file existence before loading +- Provides fallback UI for missing files +- Prevents crashes from invalid file access + +## Usage Examples + +### Direct File Access +``` +/app/editor/Invoice-Jan-2024 → Opens "Invoice-Jan-2024" +/app/editor/My%20Monthly%20Bill → Opens "My Monthly Bill" +``` + +### File Creation Flow +1. User clicks template in FilesPage +2. Enters filename: "Q1 Report" +3. App creates file and navigates to `/app/editor/Q1%20Report` +4. Editor opens with new file loaded + +### Error Handling +1. User visits `/app/editor/NonExistentFile` +2. Home component checks if "NonExistentFile" exists +3. File not found → Shows error UI +4. User clicks "Go to File Explorer" → Redirects to `/app/files` diff --git a/src/App.tsx b/src/App.tsx index 4753a60..536e5df 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,16 +1,10 @@ import { IonApp, IonRouterOutlet, - IonTabBar, - IonTabButton, - IonTabs, - IonIcon, - IonLabel, setupIonicReact, } from "@ionic/react"; import { IonReactRouter } from "@ionic/react-router"; import { Route, Redirect } from "react-router-dom"; -import { documentText, folder, menu, settings, home } from "ionicons/icons"; import Home from "./pages/Home"; import FilesPage from "./pages/FilesPage"; import SettingsPage from "./pages/SettingsPage"; @@ -21,6 +15,8 @@ import PWAUpdatePrompt from "./components/PWAUpdatePrompt"; import OfflineIndicator from "./components/OfflineIndicator"; import { usePWA } from "./hooks/usePWA"; import { isNewUser } from "./utils/helper"; +import { TemplateInitializer } from "./utils/templateInitializer"; +import { useEffect } from "react"; /* Core CSS required for Ionic components to work properly */ import "@ionic/react/css/core.css"; @@ -49,6 +45,22 @@ const AppContent: React.FC = () => { const { isOnline } = usePWA(); const showLandingPage = isNewUser(); + // Initialize multi-template architecture + useEffect(() => { + const initializeApp = async () => { + try { + const isInitialized = await TemplateInitializer.isInitialized(); + if (!isInitialized) { + await TemplateInitializer.initializeApp(); + } + } catch (error) { + console.error('Failed to initialize template system:', error); + } + }; + + initializeApp(); + }, []); + return ( @@ -58,44 +70,28 @@ const AppContent: React.FC = () => { {showLandingPage ? ( ) : ( - + )} - - {!isOnline && } - - - - - - - - - - - - - - - - - - - Editor - - - - - Files - - - - - Settings - - - + {!isOnline && } + + + + + + + + + + + + + + + + + diff --git a/src/app-data.ts b/src/app-data.ts deleted file mode 100644 index 21c3659..0000000 --- a/src/app-data.ts +++ /dev/null @@ -1,474 +0,0 @@ -export let APP_NAME = "Invoice Suite"; - -export let DATA = { - home: { - App: { - msc: { - numsheets: 5, - currentid: "sheet1", - currentname: "inv1", - sheetArr: { - sheet1: { - sheetstr: { - savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:10\ncell:C1:b:::2::l:1:f:10\ncell:D1:b:::2::l:1:f:10\ncell:E1:b:::2::l:1:f:10\ncell:F1:b:::2::l:1:f:10\ncell:G1:b:::2::l:1:f:10\ncell:A2:b::2:::l:1:f:10\ncell:B2:t:INVOICE:b:1:1:1:1:f:13:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:10\ncell:D2:t::b:2::2::l:1:f:10\ncell:E2:t::b:2::2::l:1:f:10\ncell:F2:t::b:2::2::l:1:f:10\ncell:G2:t::b:2::2::l:1:f:10\ncell:A3:b::2:::l:3:f:10\ncell:B3:b:2:::2:l:3:f:10\ncell:C3:b:2::::l:1:f:10\ncell:D3:b:2::::l:1:f:10\ncell:E3:b:2::::l:1:f:10\ncell:F3:b:2::::l:1:f:10\ncell:G3:b:2:2:::l:3:f:10\ncell:A4:b::2:::l:3:f:10\ncell:B4:b::::2:l:3:f:6\ncell:C4:t:BILL TO\\c:f:9:colspan:2\ncell:E4:f:5:cf:2\ncell:G4:b::2:::l:3:f:10\ncell:A5:b::2:::l:3:f:10\ncell:B5:b::::2:l:3:f:7\ncell:C5:t:[Name]:f:9:cf:2:colspan:3\ncell:E5:f:1:cf:2\ncell:F5:t::l:2:f:10:tvf:4:rowspan:6\ncell:G5:t::b::1:::l:2:f:10\ncell:A6:b::2:::l:3:f:10\ncell:B6:b::::2:l:3:f:7\ncell:C6:t:[Street Address]:f:9:cf:2:colspan:3\ncell:E6:f:1:cf:2\ncell:F6:l:2:f:10\ncell:G6:b::1:::l:2:f:10\ncell:A7:b::2:::l:3:f:10\ncell:B7:b::::2:l:3:f:7\ncell:C7:t:[City, State, Zip]:f:9:cf:2:colspan:3\ncell:E7:f:1:cf:2\ncell:G7:b::2:::l:3:f:10\ncell:A8:b::2:::l:3:f:10\ncell:B8:b::::2:l:3:f:7\ncell:C8:t:Phone\\c:f:9:cf:2:colspan:3\ncell:D8:f:8:cf:2\ncell:E8:f:1:cf:2\ncell:G8:b::2:::l:3:f:10\ncell:A9:b::2:::l:3:f:10\ncell:B9:b::::2:l:3:f:7\ncell:C9:t:Email\\c:f:9:cf:2:colspan:3\ncell:E9:f:1:cf:2\ncell:G9:b::2:::l:3:f:10\ncell:A10:b::2:::l:3:f:10\ncell:B10:b::::2:l:3:f:7\ncell:C10:colspan:2\ncell:G10:b::2:::l:3:f:10\ncell:A11:b::2:::l:3:f:10\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:FROM\\c:f:9:colspan:2\ncell:E11:f:8:colspan:2\ncell:G11:b::2:::l:3:f:10\ncell:A12:b::2:::l:3:f:10\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[Name]:f:9:colspan:4\ncell:E12:colspan:2\ncell:G12:b::2:::l:3:f:10\ncell:A13:b::2:::l:3:f:10\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:[Street Address]:f:9:colspan:4\ncell:E13:colspan:2\ncell:G13:b::2:::l:3:f:10\ncell:A14:b::2:::l:3:f:10\ncell:B14:b::::2:l:3:f:7\ncell:C14:t:[City, State, Zip]:f:9:colspan:4\ncell:E14:colspan:2\ncell:G14:b::2:::l:3:f:10\ncell:A15:b::2:::l:3:f:10\ncell:B15:b::::2:l:3:f:7\ncell:C15:t:Phone\\c:f:9:colspan:4\ncell:D15:f:8:cf:2:colspan:3\ncell:E15:colspan:2\ncell:G15:b::2:::l:3:f:10\ncell:A16:b::2:::l:3:f:10\ncell:B16:b::::2:l:3:f:7\ncell:C16:t:Email\\c:f:9:colspan:4\ncell:E16:colspan:2\ncell:G16:b::2:::l:3:f:10\ncell:A17:b::2:::l:3:f:10\ncell:B17:b::::2:l:3:f:6\ncell:G17:b::2:::l:3:f:10\ncell:A18:b::2:::l:3:f:10\ncell:B18:b::::2:l:3:f:10\ncell:C18:t:INVOICE #\\c:f:9:cf:2:colspan:4\ncell:D18:v:1:l:4:f:9:cf:2:colspan:3\ncell:G18:b::2:::l:3:f:10\ncell:A19:b::2:::l:3:f:10\ncell:B19:b::::2:l:3:f:10\ncell:G19:b::2:::l:3:f:10\ncell:A20:b::2:::l:3:f:10\ncell:B20:b::::2:l:3:f:10\ncell:C20:t:DATE\\c:f:9:cf:2\ncell:D20:vtf:nd:42081:TODAY():f:9:cf:2:ntvf:3:colspan:2\ncell:G20:b::2:::l:3:f:10\ncell:A21:b::2:::l:3:f:10\ncell:B21:b::::2:l:3:f:6:cf:2\ncell:C21:b:::2::l:1:f:10\ncell:D21:b:::2::l:3:f:6:cf:2\ncell:E21:b:::2::l:1:f:10\ncell:F21:b:::2::l:1:f:10\ncell:G21:b::2:::l:3:f:10\ncell:A22:b::2:::l:3:f:10\ncell:B22:b::2::2:l:3:f:10\ncell:C22:t:Description:b:1::1:1:f:9:cf:1:colspan:3\ncell:D22:t:Description:b:1::1::l:3:f:6:cf:2:colspan:2:rowspan:1\ncell:E22:t::b:2:2:2::l:1:f:10\ncell:F22:t:Amount:b:1:1:1:1:f:9:cf:1\ncell:G22:b::2::2:l:3:f:10\ncell:A23:b::2:::l:3:f:10\ncell:B23:b::2::2:l:3:f:10\ncell:C23:b:1:1::1:f:2:cf:2:colspan:3\ncell:D23:b:1:1:::l:3:f:10:cf:2:colspan:2:rowspan:1\ncell:E23:t::b:2:2:2::l:1:f:10\ncell:F23:b:1:1:::f:2:ntvf:1\ncell:G23:b::2::2:l:3:f:10\ncell:A24:b::2:::l:3:f:10\ncell:B24:b::2::2:l:3:f:10:cf:2\ncell:C24:b::1::1:f:2:cf:2:colspan:3\ncell:D24:b::1:::l:1:f:10:colspan:2\ncell:E24:l:1:f:10\ncell:F24:b::1:::f:2:ntvf:1\ncell:G24:b::2::2:l:3:f:10\ncell:A25:b::2:::l:3:f:10\ncell:B25:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C25:b::1::1:f:2:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:2:ntvf:1\ncell:G25:b::2::2:l:3:f:10\ncell:A26:b::2:::l:3:f:10\ncell:B26:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C26:b::1::1:f:2:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:2:ntvf:1\ncell:G26:b::2::2:l:3:f:10\ncell:A27:b::2:::l:3:f:10\ncell:B27:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C27:b::1::1:f:2:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:2:ntvf:1\ncell:G27:b::2::2:l:3:f:10\ncell:A28:b::2:::l:3:f:10\ncell:B28:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C28:b::1::1:f:2:cf:2:colspan:3\ncell:D28:b::1:::colspan:2\ncell:F28:b::1:::f:2:ntvf:1\ncell:G28:b::2::2:l:3:f:10\ncell:A29:b::2:::l:3:f:10\ncell:B29:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C29:b::1::1:f:2:cf:2:colspan:3\ncell:D29:b::1:::colspan:2\ncell:F29:b::1:::f:2:ntvf:1\ncell:G29:b::2::2:l:3:f:10\ncell:A30:b::2:::l:3:f:10\ncell:B30:b::2::2:l:3:f:10:cf:2\ncell:C30:b::1::1:f:2:cf:2:colspan:3\ncell:D30:b::1:::colspan:2\ncell:F30:b::1:::f:2:ntvf:1\ncell:G30:b::2::2:l:3:f:10\ncell:A31:b::2:::l:3:f:10\ncell:B31:b::2::2:l:3:f:10:cf:2\ncell:C31:b::1::1:f:2:cf:2:colspan:3\ncell:D31:b::1:::colspan:2\ncell:F31:b::1:::f:2:ntvf:1\ncell:G31:b::2::2:l:3:f:10\ncell:A32:b::2:::l:3:f:10\ncell:B32:b::2::2:l:3:f:10:cf:2\ncell:C32:b::1::1:f:2:cf:2:colspan:3\ncell:D32:b::1:::colspan:2\ncell:F32:b::1:::f:2:ntvf:1\ncell:G32:b::2::2:l:3:f:10\ncell:A33:b::2:::l:3:f:10\ncell:B33:b::2::2:l:3:f:10:cf:2\ncell:C33:b::1::1:f:2:cf:2:colspan:3\ncell:D33:b::1:::colspan:2\ncell:F33:b::1:::f:2:ntvf:1\ncell:G33:b::2::2:l:3:f:10\ncell:A34:b::2:::l:3:f:10\ncell:B34:b::2::2:l:3:f:10:cf:2\ncell:C34:b::1::1:f:2:cf:2:colspan:3\ncell:D34:b::1:::colspan:2\ncell:F34:b::1:::f:2:ntvf:1\ncell:G34:b::2::2:l:3:f:10\ncell:A35:b::2:::l:3:f:7\ncell:B35:b::2::2:l:3:f:10:cf:2\ncell:C35:b::1:1:1:f:2:cf:2:colspan:3\ncell:D35:b::1:1::l:3:f:10:cf:2:colspan:2\ncell:E35:b:::2::l:3:f:10:cf:2\ncell:F35:b::1:1::f:2:ntvf:1\ncell:G35:b::2::2:l:3:f:10\ncell:A36:b::2:::l:3:f:6\ncell:B36:b::2::2:l:3:f:6\ncell:C36:t:TOTAL:b:1:1:1:1:f:11:cf:2:colspan:3\ncell:D36:b:2::2::l:3:f:12:cf:2\ncell:E36:b:2:2:2::l:3:f:12:cf:2\ncell:F36:vtf:n:0:SUM(F23\\cF35):b:1:1:1::f:11:ntvf:1\ncell:G36:b::2::2:l:3:f:7\ncell:A37:b::2:::l:3:f:10\ncell:B37:b::::2:l:3:f:6:cf:2\ncell:C37:b:2::::l:1:f:10\ncell:D37:b:2::::l:1:f:10\ncell:E37:b:2::::l:1:f:10\ncell:F37:b:2::::l:1:f:10\ncell:G37:b::2:::l:3:f:6\ncell:A38:b::2:::l:3:f:10\ncell:B38:b::::2:l:3:f:10\ncell:G38:b::2:::l:3:f:10\ncell:A39:b::2:::l:3:f:10\ncell:B39:b::::2:l:3:f:10:cf:2\ncell:C39:t:Thank you for your business:f:4:cf:1:colspan:4\ncell:D39:t:Thank you for your business:colspan:3\ncell:E39:t:Thank you for your business:f:3:colspan:2\ncell:F39:t::l:2:f:10\ncell:G39:b::2:::l:3:f:10\ncell:A40:b::2:::l:3:f:10\ncell:B40:b::::2:l:3:f:10\ncell:G40:b::2:::l:3:f:10\ncell:A41:b::2:::l:3:f:10\ncell:B41:b:::2:2:l:3:f:10\ncell:C41:b:::2::l:3:f:10\ncell:D41:b:::2::l:3:f:10\ncell:E41:b:::2::l:3:f:10\ncell:F41:b:::2::l:3:f:10\ncell:G41:b::2:2::l:3:f:10\ncell:B42:b:1:::\ncell:C42:b:1:::\ncell:D42:b:1:::\ncell:E42:b:1:::\ncell:F42:b:1:::\ncell:G42:b:1:::\ncol:A:w:10\ncol:B:w:10\ncol:C:w:62\ncol:D:w:73\ncol:E:w:15\ncol:F:w:68\ncol:G:w:10\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:15.75\nrow:36:h:15.75\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:41:h:14.25\nsheet:c:7:r:42:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:* 9pt Trebuchet MS\nfont:3:italic bold * Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Arial\nfont:7:normal bold 12pt Arial\nfont:8:normal normal * *\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 11pt Trebuchet MS\nfont:12:normal normal 12pt Arial\nfont:13:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nlayout:4:padding:* * * 4px;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", - }, - name: "inv1", - hidden: "0", - }, - sheet2: { - sheetstr: { - savestr: - "version:1.5\ncell:B2:t:INVOICE:l:1:f:9:cf:1:colspan:6\ncell:C2:t::l:2:f:13\ncell:D2:t::l:2:f:13\ncell:E2:l:1:f:10:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:8:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:13\ncell:B3:f:6:cf:2:colspan:4\ncell:C3:t::l:2:f:13\ncell:D3:t::l:2:f:13\ncell:F3:l:1:f:7:cf:2\ncell:G3:l:1:f:14:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:14:cf:2\ncell:G4:vtf:nd:42081:TODAY():l:1:f:14:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:3:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:7:cf:2:colspan:2\ncell:G5:l:1:f:14:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:7:cf:2:colspan:2\ncell:G6:l:1:f:14:cf:2\ncell:B7:t:FROM\\c:f:12\ncell:F7:l:1:f:7:cf:2:tvf:4:colspan:2:rowspan:6\ncell:G7:l:1:f:14:cf:2\ncell:B8:t:[Company Name]:f:3:colspan:4\ncell:F8:l:1:f:7:cf:2:colspan:2\ncell:G8:l:1:f:14:cf:2\ncell:B9:t:[Street Address]:f:1:cf:2:colspan:4\ncell:F9:l:1:f:7\ncell:G9:l:1:f:14:cf:1\ncell:B10:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:G10:l:1:f:13\ncell:B11:t:Phone\\c :f:1:cf:2:colspan:4\ncell:B12:t:Email\\c:f:1:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:11:cf:2\ncell:B15:t:[Name]:f:1:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:1:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:1:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:1:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:1:cf:2:colspan:6\ncell:B20:t:Email\\c:f:1:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:14:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:13\ncell:D22:t::l:2:f:13\ncell:E22:t::l:2:f:13\ncell:F22:t::l:2:f:13\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:14:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:b:1:1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:4\ncell:D23:t::l:2:f:4\ncell:E23:t::l:2:f:4\ncell:F23:t::b::2:::l:1:f:4\ncell:G23:b::1::1:f:4:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:4\ncell:D24:t::l:2:f:4\ncell:E24:t::l:2:f:4\ncell:F24:t::b::2:::l:1:f:4\ncell:G24:b::1::1:f:4:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:4\ncell:D25:t::l:2:f:4\ncell:E25:t::l:2:f:4\ncell:F25:t::b::2:::l:1:f:4\ncell:G25:b::1::1:f:4:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:4\ncell:D26:t::l:2:f:4\ncell:E26:t::l:2:f:4\ncell:F26:t::b::2:::l:1:f:4\ncell:G26:b::1::1:f:4:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:4\ncell:D27:t::l:2:f:4\ncell:E27:t::l:2:f:4\ncell:F27:t::b::2:::l:1:f:4\ncell:G27:b::1::1:f:4:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:4\ncell:D28:t::l:2:f:4\ncell:E28:t::l:2:f:4\ncell:F28:t::b::2:::l:1:f:4\ncell:G28:b::1::1:f:4:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:4\ncell:D29:t::l:2:f:4\ncell:E29:t::l:2:f:4\ncell:F29:t::b::2:::l:1:f:4\ncell:G29:b::1::1:f:4:ntvf:1\ncell:A30:b::1::\ncell:B30:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:4\ncell:D30:t::l:2:f:4\ncell:E30:t::l:2:f:4\ncell:F30:t::b::2:::l:1:f:4\ncell:G30:b::1::1:f:4:ntvf:1\ncell:A31:b::1::\ncell:B31:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:4\ncell:D31:t::l:2:f:4\ncell:E31:t::l:2:f:4\ncell:F31:t::b::2:::l:1:f:4\ncell:G31:b::1::1:f:4:ntvf:1\ncell:A32:b::1::\ncell:B32:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:4\ncell:D32:t::l:2:f:4\ncell:E32:t::l:2:f:4\ncell:F32:t::b::2:::l:1:f:4\ncell:G32:b::1::1:f:4:ntvf:1\ncell:A33:b::1::\ncell:B33:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C33:t::l:2:f:4\ncell:D33:t::l:2:f:4\ncell:E33:t::l:2:f:4\ncell:F33:t::b::2:::l:1:f:4\ncell:G33:b::1::1:f:4:ntvf:1\ncell:A34:b::1::\ncell:B34:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C34:l:2:f:4\ncell:D34:l:2:f:4\ncell:E34:l:2:f:4\ncell:F34:b::2:::l:1:f:4\ncell:G34:b::1::1:f:4:ntvf:1\ncell:A35:b::1::\ncell:B35:b::1:1:1:f:4:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:4\ncell:D35:t::b:::2::l:1:f:4\ncell:E35:t::b:::2::l:1:f:4\ncell:F35:t::b::2:2::l:1:f:4\ncell:G35:b::1:1:1:f:4:ntvf:1\ncell:B36:b:2::::l:1:f:13\ncell:C36:b:2::::l:1:f:13\ncell:D36:b:2::::l:1:f:13\ncell:E36:b:2::::l:1:f:14\ncell:F36:t:Subtotal:b:2::::l:1:f:11\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:11:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:14:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:13\ncell:D37:t::b:::2::l:1:f:13\ncell:F37:t:Tax Rate:l:1:f:11\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:13\ncell:D38:t::b:2::::l:1:f:13\ncell:F38:t:Tax:l:1:f:11\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:13\ncell:D39:t::l:2:f:13\ncell:F39:t:Other:b:::1::l:1:f:11\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:13\ncell:D40:t::l:2:f:13\ncell:F40:t:TOTAL:b:1::::l:1:f:14\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:11:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:5:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:13\ncell:D43:t::l:2:f:13\ncell:E43:t::l:2:f:13\ncell:F43:t::l:2:f:13\ncell:G43:t::l:2:f:13\ncol:A:w:10\ncol:B:w:90\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:45\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 10pt Trebuchet MS\nfont:4:* 9pt Trebuchet MS\nfont:5:italic bold 10pt Trebuchet MS\nfont:6:italic normal * Trebuchet MS\nfont:7:normal bold 10pt Trebuchet MS\nfont:8:normal bold 14pt Trebuchet MS\nfont:9:normal bold 16pt Trebuchet MS\nfont:10:normal bold 28pt Trebuchet MS\nfont:11:normal normal * Trebuchet MS\nfont:12:normal normal 10pt *\nfont:13:normal normal 10pt Arial\nfont:14:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", - }, - name: "inv2", - hidden: "0", - }, - sheet3: { - sheetstr: { - savestr: - 'version:1.5\ncell:B2:t:INVOICE:l:1:f:12:cf:1:colspan:6\ncell:C2:t::l:2:f:10\ncell:D2:t::l:2:f:10\ncell:E2:l:1:f:8:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:10\ncell:B3:f:5:cf:2:colspan:4\ncell:C3:t::l:2:f:10\ncell:D3:t::l:2:f:10\ncell:F3:l:1:f:6:cf:2\ncell:G3:l:1:f:11:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:11:cf:2\ncell:G4:vtf:nd:42081:TODAY():l:1:f:11:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:9:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:6:cf:2:colspan:2\ncell:G5:l:1:f:11:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:6:cf:2:colspan:2\ncell:G6:l:1:f:11:cf:2\ncell:B7:t:FROM\\c:f:11\ncell:F7:l:1:f:6:cf:2:colspan:2\ncell:G7:l:1:f:11:cf:2\ncell:B8:t:[Company Name]:f:9:colspan:4\ncell:F8:l:1:f:6:cf:2:tvf:4:colspan:2:rowspan:5\ncell:G8:l:1:f:11:cf:2\ncell:B9:t:[Street Address]:f:9:cf:2:colspan:4\ncell:F9:l:1:f:6\ncell:G9:l:1:f:11:cf:1\ncell:B10:t:[City, State, Zip]:f:9:cf:2:colspan:4\ncell:G10:l:1:f:10\ncell:B11:t:Phone\\c :f:9:cf:2:colspan:4\ncell:B12:t:Email\\c:f:9:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:9:cf:2\ncell:B15:t:[Name]:f:9:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:9:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:9:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:9:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:9:cf:2:colspan:6\ncell:B20:t:Email\\c:f:9:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:11:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:10\ncell:D22:t::l:2:f:10\ncell:E22:t::l:2:f:10\ncell:F22:t::l:2:f:10\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:11:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), sheet7!B6, inv3!B6)):b:1:1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:3\ncell:D23:t::l:2:f:3\ncell:E23:t::l:2:f:3\ncell:F23:t::b::2:::l:1:f:3\ncell:G23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), (sheet7!E6*sheet7!F6), (inv3!E6*inv3!F6))):b::1::1:f:3:ntvf:1\ncell:A24:b::1::\ncell:B24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), SHEET7!B7, INV3!B7)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:3\ncell:D24:t::l:2:f:3\ncell:E24:t::l:2:f:3\ncell:F24:t::b::2:::l:1:f:3\ncell:G24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), (SHEET7!E7*SHEET7!F7), (INV3!E7*INV3!F7))):b::1::1:f:3:ntvf:1\ncell:A25:b::1::\ncell:B25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), SHEET7!B8, INV3!B8)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:3\ncell:D25:t::l:2:f:3\ncell:E25:t::l:2:f:3\ncell:F25:t::b::2:::l:1:f:3\ncell:G25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), (SHEET7!E8*SHEET7!F8), (INV3!E8*INV3!F8))):b::1::1:f:3:ntvf:1\ncell:A26:b::1::\ncell:B26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), SHEET7!B9, INV3!B9)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:3\ncell:D26:t::l:2:f:3\ncell:E26:t::l:2:f:3\ncell:F26:t::b::2:::l:1:f:3\ncell:G26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), (SHEET7!E9*SHEET7!F9), (INV3!E9*INV3!F9))):b::1::1:f:3:ntvf:1\ncell:A27:b::1::\ncell:B27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), SHEET7!B10, INV3!B10)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:3\ncell:D27:t::l:2:f:3\ncell:E27:t::l:2:f:3\ncell:F27:t::b::2:::l:1:f:3\ncell:G27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), (SHEET7!E10*SHEET7!F10), (INV3!E10*INV3!F10))):b::1::1:f:3:ntvf:1\ncell:A28:b::1::\ncell:B28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), SHEET7!B11, INV3!B11)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:3\ncell:D28:t::l:2:f:3\ncell:E28:t::l:2:f:3\ncell:F28:t::b::2:::l:1:f:3\ncell:G28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), (SHEET7!E11*SHEET7!F11), (INV3!E11*INV3!F11))):b::1::1:f:3:ntvf:1\ncell:A29:b::1::\ncell:B29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), SHEET7!B12, INV3!B12)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:3\ncell:D29:t::l:2:f:3\ncell:E29:t::l:2:f:3\ncell:F29:t::b::2:::l:1:f:3\ncell:G29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), (SHEET7!E12*SHEET7!F12), (INV3!E12*INV3!F12))):b::1::1:f:3:ntvf:1\ncell:A30:b::1::\ncell:B30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), SHEET7!B13, INV3!B13)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:3\ncell:D30:t::l:2:f:3\ncell:E30:t::l:2:f:3\ncell:F30:t::b::2:::l:1:f:3\ncell:G30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), (SHEET7!E13*SHEET7!F13), (INV3!E13*INV3!F13))):b::1::1:f:3:ntvf:1\ncell:A31:b::1::\ncell:B31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), SHEET7!B14, INV3!B14)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:3\ncell:D31:t::l:2:f:3\ncell:E31:t::l:2:f:3\ncell:F31:t::b::2:::l:1:f:3\ncell:G31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), (SHEET7!E14*SHEET7!F14), (INV3!E14*INV3!F14))):b::1::1:f:3:ntvf:1\ncell:A32:b::1::\ncell:B32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), SHEET7!B15, INV3!B15)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:3\ncell:D32:t::l:2:f:3\ncell:E32:t::l:2:f:3\ncell:F32:t::b::2:::l:1:f:3\ncell:G32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), (SHEET7!E15*SHEET7!F15), (INV3!E15*INV3!F15))):b::1::1:f:3:ntvf:1\ncell:A33:b::1::\ncell:B33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), SHEET7!B16, INV3!B16)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C33:l:2:f:3\ncell:D33:l:2:f:3\ncell:E33:l:2:f:3\ncell:F33:b::2:::l:1:f:3\ncell:G33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), (SHEET7!E16*SHEET7!F16), (INV3!E16*INV3!F16))):b::1::1:f:3:ntvf:1\ncell:A34:b::1::\ncell:B34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), SHEET7!B17, INV3!B17)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C34:t::l:2:f:3\ncell:D34:t::l:2:f:3\ncell:E34:t::l:2:f:3\ncell:F34:t::b::2:::l:1:f:3\ncell:G34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), (SHEET7!E17*SHEET7!F17), (INV3!E17*INV3!F17))):b::1::1:f:3:ntvf:1\ncell:A35:b::1::\ncell:B35:vtf:t: :IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), SHEET7!B18, INV3!B18)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:3\ncell:D35:t::b:::2::l:1:f:3\ncell:E35:t::b:::2::l:1:f:3\ncell:F35:t::b::2:2::l:1:f:3\ncell:G35:vtf:t: :IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), (SHEET7!E18*SHEET7!F18), (INV3!E18*INV3!F18))):b::1::1:f:3:ntvf:1\ncell:B36:b:2::::l:1:f:10\ncell:C36:b:2::::l:1:f:10\ncell:D36:b:2::::l:1:f:10\ncell:E36:b:2::::l:1:f:11\ncell:F36:t:Subtotal:b:2::::l:1:f:9\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:9:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:11:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:10\ncell:D37:t::b:::2::l:1:f:10\ncell:F37:t:Tax Rate:l:1:f:9\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:10\ncell:D38:t::b:2::::l:1:f:10\ncell:F38:t:Tax:l:1:f:9\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:10\ncell:D39:t::l:2:f:10\ncell:F39:t:Other:b:::1::l:1:f:9\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:10\ncell:D40:t::l:2:f:10\ncell:F40:t:TOTAL:b:1::::l:1:f:11\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:9:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:4:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:10\ncell:D43:t::l:2:f:10\ncell:E43:t::l:2:f:10\ncell:F43:t::l:2:f:10\ncell:G43:t::l:2:f:10\ncol:A:w:10\ncol:B:w:65\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:53\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 9pt Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:italic normal * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 14pt Trebuchet MS\nfont:8:normal bold 28pt Trebuchet MS\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 10pt Trebuchet MS\nfont:12:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', - }, - name: "sheet6", - hidden: "0", - }, - sheet4: { - sheetstr: { - savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Hours:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Rate:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", - }, - name: "inv3", - hidden: "0", - }, - sheet5: { - sheetstr: { - savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Qty.:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Price:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", - }, - name: "sheet7", - hidden: "0", - }, - }, - EditableCells: { - allow: true, - cells: { - "inv1!B2": true, - "inv1!C5": true, - "inv1!C6": true, - "inv1!C7": true, - "inv1!C8": true, - "inv1!C9": true, - "inv1!C11": true, - "inv1!C12": true, - "inv1!C13": true, - "inv1!C14": true, - "inv1!C15": true, - "inv1!C16": true, - "inv1!C18": true, - "inv1!C20": true, - "inv1!D20": true, - "inv1!C23": true, - "inv1!C24": true, - "inv1!C25": true, - "inv1!C26": true, - "inv1!C27": true, - "inv1!C28": true, - "inv1!C29": true, - "inv1!C30": true, - "inv1!C31": true, - "inv1!C32": true, - "inv1!C33": true, - "inv1!C34": true, - "inv1!C35": true, - "inv1!F23": true, - "inv1!F24": true, - "inv1!F25": true, - "inv1!F26": true, - "inv1!F27": true, - "inv1!F28": true, - "inv1!F29": true, - "inv1!F30": true, - "inv1!F31": true, - "inv1!F32": true, - "inv1!F33": true, - "inv1!F34": true, - "inv1!F35": true, - "inv2!B2": true, - "inv2!F4": true, - "inv2!G4": true, - "inv2!B5": true, - "inv2!B7": true, - "inv2!B8": true, - "inv2!B9": true, - "inv2!B10": true, - "inv2!B11": true, - "inv2!B12": true, - "inv2!B14": true, - "inv2!B15": true, - "inv2!B16": true, - "inv2!B17": true, - "inv2!B18": true, - "inv2!B19": true, - "inv2!B20": true, - "inv2!B23": true, - "inv2!B24": true, - "inv2!B25": true, - "inv2!B26": true, - "inv2!B27": true, - "inv2!B28": true, - "inv2!B29": true, - "inv2!B30": true, - "inv2!B31": true, - "inv2!B32": true, - "inv2!B33": true, - "inv2!B34": true, - "inv2!B35": true, - "inv2!G23": true, - "inv2!G24": true, - "inv2!G25": true, - "inv2!G26": true, - "inv2!G27": true, - "inv2!G28": true, - "inv2!G29": true, - "inv2!G30": true, - "inv2!G31": true, - "inv2!G32": true, - "inv2!G33": true, - "inv2!G34": true, - "inv2!G35": true, - "inv2!B38": true, - "inv2!B39": true, - "inv2!B40": true, - "inv2!F36": true, - "inv2!G37": true, - "inv2!F37": true, - "inv2!F39": true, - "inv2!G39": true, - "inv2!F38": true, - "sheet6!B2": true, - "sheet6!F4": true, - "sheet6!G4": true, - "sheet6!B5": true, - "sheet6!B7": true, - "sheet6!B8": true, - "sheet6!B9": true, - "sheet6!B10": true, - "sheet6!B11": true, - "sheet6!B12": true, - "sheet6!B14": true, - "sheet6!B15": true, - "sheet6!B16": true, - "sheet6!B17": true, - "sheet6!B18": true, - "sheet6!B19": true, - "sheet6!B20": true, - "sheet6!B38": true, - "sheet6!B39": true, - "sheet6!B40": true, - "sheet6!F36": true, - "sheet6!G37": true, - "sheet6!F37": true, - "sheet6!F39": true, - "sheet6!G39": true, - "sheet6!F38": true, - "inv3!B2": true, - "inv3!B6": true, - "inv3!B7": true, - "inv3!B8": true, - "inv3!B9": true, - "inv3!B10": true, - "inv3!B11": true, - "inv3!B12": true, - "inv3!B13": true, - "inv3!B14": true, - "inv3!B15": true, - "inv3!B16": true, - "inv3!B17": true, - "inv3!B18": true, - "inv3!E6": true, - "inv3!E7": true, - "inv3!E8": true, - "inv3!E9": true, - "inv3!E10": true, - "inv3!E11": true, - "inv3!E12": true, - "inv3!E13": true, - "inv3!E14": true, - "inv3!E15": true, - "inv3!E16": true, - "inv3!E17": true, - "inv3!E18": true, - "inv3!F6": true, - "inv3!F7": true, - "inv3!F8": true, - "inv3!F9": true, - "inv3!F10": true, - "inv3!F11": true, - "inv3!F12": true, - "inv3!F13": true, - "inv3!F14": true, - "inv3!F15": true, - "inv3!F16": true, - "inv3!F17": true, - "inv3!F18": true, - "sheet7!F6": true, - "sheet7!F7": true, - "sheet7!F8": true, - "sheet7!F9": true, - "sheet7!F10": true, - "sheet7!F11": true, - "sheet7!F12": true, - "sheet7!F13": true, - "sheet7!F14": true, - "sheet7!F15": true, - "sheet7!F16": true, - "sheet7!F17": true, - "sheet7!F18": true, - "sheet7!E6": true, - "sheet7!E7": true, - "sheet7!E8": true, - "sheet7!E9": true, - "sheet7!E10": true, - "sheet7!E11": true, - "sheet7!E12": true, - "sheet7!E13": true, - "sheet7!E14": true, - "sheet7!E15": true, - "sheet7!E16": true, - "sheet7!E17": true, - "sheet7!E18": true, - "sheet7!B6": true, - "sheet7!B2": true, - "sheet7!B7": true, - "sheet7!B8": true, - "sheet7!B9": true, - "sheet7!B10": true, - "sheet7!B11": true, - "sheet7!B12": true, - "sheet7!B13": true, - "sheet7!B14": true, - "sheet7!B15": true, - "sheet7!B16": true, - "sheet7!B17": true, - "sheet7!B18": true, - "inv1!D8": true, - "inv1!D15": true, - "inv1!D18": true, - "inv2!C5": true, - "sheet6!C5": true, - }, - constraints: { - "inv1!C5": ["prompttext", "0", "1e10", "Name"], - "inv1!C6": ["prompttext", "0", "1e10", "Street Address"], - "inv1!C7": ["prompttext", "0", "1e10", "City, State, Zip"], - "inv1!C8": ["prompttext", "0", "1e10", "Phone"], - "inv1!C9": ["promptemail", "0", "1e10", "Email"], - "inv1!C11": ["prompttext", "0", "1e10", "From"], - "inv1!C12": ["prompttext", "0", "1e10", "Name"], - "inv1!C13": ["prompttext", "0", "1e10", "Street Address"], - "inv1!C14": ["prompttext", "0", "1e10", "City, State, Zip"], - "inv1!C15": ["prompttext", "0", "1e10", "Phone"], - "inv1!C16": ["promptemail", "0", "1e10", "Email"], - "inv1!C18": ["prompttext", "0", "1e10", "Invoice #"], - "inv1!C20": ["prompttext", "0", "1e10", "Date"], - "inv1!D20": ["prompttext", "0", "1e10", "Date"], - "inv1!C23": ["prompttext", "0", "1e10", "Description"], - "inv1!C24": ["prompttext", "0", "1e10", "Description"], - "inv1!C25": ["prompttext", "0", "1e10", "Description"], - "inv1!C26": ["prompttext", "0", "1e10", "Description"], - "inv1!C27": ["prompttext", "0", "1e10", "Description"], - "inv1!C28": ["prompttext", "0", "1e10", "Description"], - "inv1!C29": ["prompttext", "0", "1e10", "Description"], - "inv1!C30": ["prompttext", "0", "1e10", "Description"], - "inv1!C31": ["prompttext", "0", "1e10", "Description"], - "inv1!C32": ["prompttext", "0", "1e10", "Description"], - "inv1!C33": ["prompttext", "0", "1e10", "Description"], - "inv1!C34": ["prompttext", "0", "1e10", "Description"], - "inv1!C35": ["prompttext", "0", "1e10", "Description"], - "inv1!F23": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F24": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F25": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F26": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F27": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F28": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F29": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F30": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F31": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F32": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F33": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F34": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F35": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!B2": ["prompttext", "0", "1e10", "Invoice"], - "inv2!F4": ["prompttext", "0", "1e10", "Date"], - "inv2!G4": ["prompttext", "0", "1e10", "Date"], - "inv2!B5": ["prompttext", "0", "1e10", "Invoice #"], - "inv2!B7": ["prompttext", "0", "1e10", "From"], - "inv2!B8": ["prompttext", "0", "1e10", "Company Name"], - "inv2!B9": ["prompttext", "0", "1e10", "Street Address"], - "inv2!B10": ["prompttext", "0", "1e10", "City, State, Zip"], - "inv2!B11": ["prompttext", "0", "1e10", "Phone"], - "inv2!B12": ["promptemail", "0", "1e10", "Email"], - "inv2!B14": ["prompttext", "0", "1e10", "Bill To"], - "inv2!B15": ["prompttext", "0", "1e10", "Name"], - "inv2!B16": ["prompttext", "0", "1e10", "Company Name"], - "inv2!B17": ["prompttext", "0", "1e10", "Street Address"], - "inv2!B18": ["prompttext", "0", "1e10", "City, State, Zip"], - "inv2!B19": ["prompttext", "0", "1e10", "Phone"], - "inv2!B20": ["promptemail", "0", "1e10", "Email"], - "inv2!B23": ["prompttext", "0", "1e10", "Description"], - "inv2!B24": ["prompttext", "0", "1e10", "Description"], - "inv2!B25": ["prompttext", "0", "1e10", "Description"], - "inv2!B26": ["prompttext", "0", "1e10", "Description"], - "inv2!B27": ["prompttext", "0", "1e10", "Description"], - "inv2!B28": ["prompttext", "0", "1e10", "Description"], - "inv2!B29": ["prompttext", "0", "1e10", "Description"], - "inv2!B30": ["prompttext", "0", "1e10", "Description"], - "inv2!B31": ["prompttext", "0", "1e10", "Description"], - "inv2!B32": ["prompttext", "0", "1e10", "Description"], - "inv2!B33": ["prompttext", "0", "1e10", "Description"], - "inv2!B34": ["prompttext", "0", "1e10", "Description"], - "inv2!B35": ["prompttext", "0", "1e10", "Description"], - "inv2!G23": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G24": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G25": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G26": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G27": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G28": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G29": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G30": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G31": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G32": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G33": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G34": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!G35": ["promptdecimal", "0", "1e10", "Amount"], - "inv2!B38": ["prompttext", "0", "1e10", "Notes"], - "inv2!B39": ["prompttext", "0", "1e10", "Notes"], - "inv2!B40": ["prompttext", "0", "1e10", "Notes"], - "inv2!F36": ["prompttext", "0", "1e10", "Subtotal"], - "inv2!G37": ["promptdecimal", "0", "1e10", "Tax Rate (0.00)"], - "inv2!F37": ["prompttext", "0", "1e10", "Tax Rate"], - "inv2!F39": ["prompttext", "0", "1e10", "Other"], - "inv2!G39": ["promptdecimal", "0", "1e10", "Other"], - "inv2!F38": ["prompttext", "0", "1e10", "Tax"], - "sheet6!B2": ["prompttext", "0", "1e10", "Invoice"], - "sheet6!F4": ["prompttext", "0", "1e10", "Date"], - "sheet6!G4": ["prompttext", "0", "1e10", "Date"], - "sheet6!B5": ["prompttext", "0", "1e10", "Invoice #"], - "sheet6!B7": ["prompttext", "0", "1e10", "From"], - "sheet6!B8": ["prompttext", "0", "1e10", "Company Name"], - "sheet6!B9": ["prompttext", "0", "1e10", "Street Address"], - "sheet6!B10": ["prompttext", "0", "1e10", "City, State, Zip"], - "sheet6!B11": ["prompttext", "0", "1e10", "Phone"], - "sheet6!B12": ["promptemail", "0", "1e10", "Email"], - "sheet6!B14": ["prompttext", "0", "1e10", "Bill To"], - "sheet6!B15": ["prompttext", "0", "1e10", "Name"], - "sheet6!B16": ["prompttext", "0", "1e10", "Company Name"], - "sheet6!B17": ["prompttext", "0", "1e10", "Street Address"], - "sheet6!B18": ["prompttext", "0", "1e10", "City, State, Zip"], - "sheet6!B19": ["prompttext", "0", "1e10", "Phone"], - "sheet6!B20": ["promptemail", "0", "1e10", "Email"], - "sheet6!B38": ["prompttext", "0", "1e10", "Notes"], - "sheet6!B39": ["prompttext", "0", "1e10", "Notes"], - "sheet6!B40": ["prompttext", "0", "1e10", "Notes"], - "sheet6!F36": ["prompttext", "0", "1e10", "Subtotal"], - "sheet6!G37": ["promptdecimal", "0", "1e10", "Tax Rate (0.00)"], - "sheet6!F37": ["prompttext", "0", "1e10", "Tax Rate"], - "sheet6!F39": ["prompttext", "0", "1e10", "Other"], - "sheet6!G39": ["promptdecimal", "0", "1e10", "Other"], - "sheet6!F38": ["prompttext", "0", "1e10", "Tax"], - "inv3!B6": ["prompttext", "0", "1e10", "Description"], - "inv3!B7": ["prompttext", "0", "1e10", "Description"], - "inv3!B8": ["prompttext", "0", "1e10", "Description"], - "inv3!B9": ["prompttext", "0", "1e10", "Description"], - "inv3!B10": ["prompttext", "0", "1e10", "Description"], - "inv3!B11": ["prompttext", "0", "1e10", "Description"], - "inv3!B12": ["prompttext", "0", "1e10", "Description"], - "inv3!B13": ["prompttext", "0", "1e10", "Description"], - "inv3!B14": ["prompttext", "0", "1e10", "Description"], - "inv3!B15": ["prompttext", "0", "1e10", "Description"], - "inv3!B16": ["prompttext", "0", "1e10", "Description"], - "inv3!B17": ["prompttext", "0", "1e10", "Description"], - "inv3!B18": ["prompttext", "0", "1e10", "Description"], - "sheet7!B6": ["prompttext", "0", "1e10", "Description"], - "sheet7!B7": ["prompttext", "0", "1e10", "Description"], - "sheet7!B8": ["prompttext", "0", "1e10", "Description"], - "sheet7!B9": ["prompttext", "0", "1e10", "Description"], - "sheet7!B10": ["prompttext", "0", "1e10", "Description"], - "sheet7!B11": ["prompttext", "0", "1e10", "Description"], - "sheet7!B12": ["prompttext", "0", "1e10", "Description"], - "sheet7!B13": ["prompttext", "0", "1e10", "Description"], - "sheet7!B14": ["prompttext", "0", "1e10", "Description"], - "sheet7!B15": ["prompttext", "0", "1e10", "Description"], - "sheet7!B16": ["prompttext", "0", "1e10", "Description"], - "sheet7!B17": ["prompttext", "0", "1e10", "Description"], - "sheet7!B18": ["prompttext", "0", "1e10", "Description"], - "inv1!D8": ["prompttext", "0", "1e10", "Phone"], - "inv1!D15": ["prompttext", "0", "1e10", "Phone"], - "inv1!D18": ["promptnumeric", "0", "1e10", "Invoice#"], - "inv2!C5": ["promptnumeric", "0", "1e10", "Invoice#"], - "sheet6!C5": ["promptnumeric", "0", "1e10", "Invoice#"], - "sheet7!E6": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E7": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E8": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E9": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E10": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E11": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E12": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E13": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E14": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E15": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E16": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E17": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E18": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!F6": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F7": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F8": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F9": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F10": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F11": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F12": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F13": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F14": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F15": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F16": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F17": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F18": ["promptdecimal", "0", "1e10", "Price"], - "inv3!E6": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E7": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E8": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E9": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E10": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E11": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E12": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E13": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E14": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E15": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E16": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E17": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E18": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!F6": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F7": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F8": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F9": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F10": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F11": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F12": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F13": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F14": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F15": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F16": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F17": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F18": ["promptdecimal", "0", "1e10", "Rate"], - }, - }, - }, - footers: [ - { name: "Type1", index: 1, isActive: true }, - { name: "Type2", index: 2, isActive: false }, - { name: "Type3", index: 3, isActive: false }, - { name: "Detail1", index: 4, isActive: false }, - { name: "Detail2", index: 5, isActive: false }, - ], - }, - }, -}; diff --git a/src/components/FileMenu/FileOptions.tsx b/src/components/FileMenu/FileOptions.tsx index 9c6f907..f553906 100644 --- a/src/components/FileMenu/FileOptions.tsx +++ b/src/components/FileMenu/FileOptions.tsx @@ -47,7 +47,7 @@ import { } from "ionicons/icons"; import * as AppGeneral from "../socialcalc/index.js"; import { File } from "../Storage/LocalStorage.js"; -import { DATA } from "../../app-data.js"; +import { DATA } from "../../templates.js"; import { useInvoice } from "../../contexts/InvoiceContext.js"; import { formatDateForFilename } from "../../utils/helper.js"; import { useTheme } from "../../contexts/ThemeContext.js"; @@ -312,6 +312,7 @@ const FileOptions: React.FC = ({ setShowUnsavedChangesAlert(true); } }; + const createNewFile = async () => { try { // Reset to defaults first @@ -345,6 +346,7 @@ const FileOptions: React.FC = ({ setShowToast(true); } }; + const handleDiscardAndCreateNew = async () => { try { // User confirmed to discard changes, proceed with creating new file diff --git a/src/components/Files/Files.tsx b/src/components/Files/Files.tsx index d7a400e..577dfc1 100644 --- a/src/components/Files/Files.tsx +++ b/src/components/Files/Files.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import "./Files.css"; import * as AppGeneral from "../socialcalc/index.js"; -import { DATA } from "../../app-data.js"; +import { DATA } from "../../templates.js"; import { File as LocalFile, Local } from "../Storage/LocalStorage"; import { IonIcon, @@ -27,6 +27,7 @@ import { IonCardContent, IonCardHeader, IonCardTitle, + IonCardSubtitle, IonModal, IonFab, IonFabButton, @@ -35,6 +36,10 @@ import { IonItemSliding, IonItemOptions, IonItemOption, + IonChip, + IonGrid, + IonRow, + IonCol, } from "@ionic/react"; import { trash, @@ -51,6 +56,9 @@ import { swapVertical, create, ellipsisHorizontal, + add, + layers, + copyOutline, } from "ionicons/icons"; import { useTheme } from "../../contexts/ThemeContext"; import { useHistory } from "react-router-dom"; @@ -60,6 +68,8 @@ import { isQuotaExceededError, getQuotaExceededMessage, } from "../../utils/helper"; +import { TemplateManager } from "../../utils/templateManager"; +import { TemplateInitializer } from "../../utils/templateInitializer"; const Files: React.FC<{ store: Local; @@ -67,7 +77,8 @@ const Files: React.FC<{ updateSelectedFile: Function; updateBillType: Function; }> = (props) => { - const { selectedFile, updateSelectedFile } = useInvoice(); + const { selectedFile, updateSelectedFile, activeTempId, updateActiveTempId } = + useInvoice(); const { isDarkMode } = useTheme(); const history = useHistory(); @@ -91,66 +102,32 @@ const Files: React.FC<{ const [serverFilesLoading, setServerFilesLoading] = useState(false); + // Template selection states + const [selectedTemplateFilter, setSelectedTemplateFilter] = useState< + number | "all" + >("all"); + // Blockchain state removed - local-only mode - const handleSaveUnsavedChanges = async () => { - // Save Default File Changes if not already saved - if (selectedFile === "default" && props.file !== "default") { - try { - const defaultExists = await props.store._checkKey("default"); - if (defaultExists) { - const storedDefaultFile = await props.store._getFile("default"); - - // Decode the stored content - const storedContent = decodeURIComponent(storedDefaultFile.content); - const msc = DATA["home"]["App"]["msc"]; - - const hasUnsavedChanges = storedContent !== JSON.stringify(msc); - if (hasUnsavedChanges) { - console.log("Default file has unsaved changes, saving..."); - // Save the current spreadsheet content to the default file - const currentContent = AppGeneral.getSpreadsheetContent(); - const now = new Date().toISOString(); - - const untitledFileName = - "Untitled-" + formatDateForFilename(new Date()); - const updatedDefaultFile = new LocalFile( - now, // created - now, // modified - encodeURIComponent(currentContent), // encoded content - untitledFileName, // new name for the default file - storedDefaultFile.billType, // keep the same billType - false // isEncrypted = false for default files - ); - await props.store._saveFile(updatedDefaultFile); - - // Clear Default File... - const templateContent = encodeURIComponent(JSON.stringify(msc)); - const newDefaultFile = new LocalFile( - now, - now, - templateContent, - "default", - 1 - ); - await props.store._saveFile(newDefaultFile); - setToastMessage(`Changes Saved as ${untitledFileName}`); - setShowToast(true); - } - } - } catch (error) { - console.error("Error saving default file changes:", error); + // Template helper functions + const getAvailableTemplates = () => { + return TemplateInitializer.getAllTemplates(); + }; - // Check if the error is due to storage quota exceeded - if (isQuotaExceededError(error)) { - setToastMessage(getQuotaExceededMessage("saving changes")); - } else { - setToastMessage("Failed to save default file changes"); - } - setShowToast(true); - } - } + const getTemplateInfo = (templateId: number) => { + const template = TemplateInitializer.getTemplate(templateId); + return template ? template.template : `Template ${templateId}`; + }; + + const getFileTemplateInfo = (fileData: any) => { + return fileData.templateMetadata || null; }; + + const handleSaveUnsavedChanges = async () => { + // No longer need to handle default file changes + // since we removed the default file concept + }; + // Edit local file const editFile = async (key: string) => { try { @@ -158,62 +135,11 @@ const Files: React.FC<{ await handleSaveUnsavedChanges(); - const data = await props.store._getFile(key); - console.log("File data retrieved:", { - name: data.name, - contentLength: data.content?.length, - billType: data.billType, - hasContent: !!data.content, - }); - - if (!data.content) { - setToastMessage("File content is empty or corrupted"); - setShowToast(true); - return; - } - // console.log("billType:", data.billType); - // console.log("File CONTENT-------------->", data.content); - const decodedContent = decodeURIComponent(data.content); - // console.log("Decoded content:", decodedContent); - // console.log("Decoded content length:", decodedContent.length); - // console.log("Decoded content preview:", decodedContent.substring(0, 200)); - - // Ensure SocialCalc is properly initialized before loading the file - // First, try to get the current workbook control to see if it's initialized - try { - const currentControl = AppGeneral.getWorkbookInfo(); - console.log("Current workbook info:", currentControl); - - if (currentControl && currentControl.workbook) { - // SocialCalc is initialized, use viewFile - AppGeneral.viewFile(key, decodedContent); - console.log("File loaded successfully with viewFile"); - } else { - // SocialCalc not initialized, initialize it first - console.log("SocialCalc not initialized, initializing..."); - AppGeneral.initializeApp(decodedContent); - console.log("File loaded successfully with initializeApp"); - } - } catch (error) { - console.error("Error checking SocialCalc state:", error); - // Fallback: try to initialize the app - try { - AppGeneral.initializeApp(decodedContent); - console.log("File loaded successfully with initializeApp (fallback)"); - } catch (initError) { - console.error("initializeApp failed:", initError); - throw new Error( - "Failed to load file: SocialCalc initialization error" - ); - } - } - - props.updateSelectedFile(key); - props.updateBillType(data.billType); - history.push("/app/editor"); + // Simply navigate to the editor - let Home.tsx handle file loading and SocialCalc initialization + history.push(`/app/editor/${encodeURIComponent(key)}`); } catch (error) { console.error("Error in editFile:", error); - setToastMessage("Failed to load file"); + setToastMessage("Failed to navigate to editor"); setShowToast(true); } }; @@ -224,13 +150,6 @@ const Files: React.FC<{ setCurrentKey(key); }; - // Load default file - const loadDefault = () => { - const msc = DATA["home"]["App"]["msc"]; - AppGeneral.viewFile("default", JSON.stringify(msc)); - props.updateSelectedFile("default"); - }; - // Format date with validation const _formatDate = (date: string) => { if (!date) return "Unknown date"; @@ -399,9 +318,9 @@ const Files: React.FC<{ // Validation function (adapted from Menu.tsx) const _validateName = async (filename: string, excludeKey?: string) => { filename = filename.trim(); - if (filename === "default" || filename === "Untitled") { + if (filename === "Untitled") { setToastMessage( - "Cannot update default or Untitled file! Use Save As Button to save." + "Cannot update Untitled file! Use Save As Button to save." ); return false; } else if (filename === "" || !filename) { @@ -511,7 +430,6 @@ const Files: React.FC<{ const localFiles = await props.store._getAllFiles(); const filesArray = Object.keys(localFiles) - .filter((key) => key !== "default") // Exclude the default file from the list .map((key) => { const fileData = localFiles[key]; @@ -538,18 +456,32 @@ const Files: React.FC<{ dateCreated: createdDate, dateModified: modifiedDate, type: "local", + templateMetadata: fileData.templateMetadata || null, }; }); - const filteredFiles = filterFilesBySearch(filesArray, searchQuery); + + // Filter by template if a specific template is selected + let filteredFiles = filesArray; + if (selectedTemplateFilter !== "all") { + filteredFiles = filesArray.filter( + (file) => file.templateMetadata?.templateId === selectedTemplateFilter + ); + } + + // Apply search filter + filteredFiles = filterFilesBySearch(filteredFiles, searchQuery); + if (filteredFiles.length === 0) { + const emptyMessage = searchQuery.trim() + ? `No files found matching "${searchQuery}"` + : selectedTemplateFilter !== "all" + ? `No files found for ${getTemplateInfo(selectedTemplateFilter as number)}` + : "No local files found"; + content = ( - - {searchQuery.trim() - ? `No files found matching "${searchQuery}"` - : "No local files found"} - + {emptyMessage} ); @@ -581,6 +513,21 @@ const Files: React.FC<{ Local file • {getLocalFileDateInfo(file).label}:{" "} {_formatDate(getLocalFileDateInfo(file).value)}

+ {file.templateMetadata && ( +
+ + + {file.templateMetadata.template} + + {file.templateMetadata.footers.length > 0 && ( + + + {file.templateMetadata.footers.length} footer(s) + + + )} +
+ )}
{(files as any[]).map((file) => ( - {" "} editFile(file.key)} @@ -663,6 +609,21 @@ const Files: React.FC<{ Local file • {getLocalFileDateInfo(file).label}:{" "} {_formatDate(getLocalFileDateInfo(file).value)}

+ {file.templateMetadata && ( +
+ + + {file.templateMetadata.template} + + {file.templateMetadata.footers.length > 0 && ( + + + {file.templateMetadata.footers.length} footer(s) + + + )} +
+ )}
{ renderFileList(); // eslint-disable-next-line - }, [props.file, fileSource, searchQuery, sortBy, serverFilesLoading]); + }, [props.file, fileSource, searchQuery, sortBy, serverFilesLoading, selectedTemplateFilter]); // Reset sort option when switching file sources to ensure compatibility useEffect(() => { @@ -747,6 +708,7 @@ const Files: React.FC<{ alignItems: "center", maxWidth: "800px", margin: "0 auto", + flexWrap: "wrap", }} > + + {/* Template Filter */} +
+ + setSelectedTemplateFilter(e.detail.value)} + style={{ + flex: "1", + "--placeholder-color": "var(--ion-color-medium)", + "--color": "var(--ion-color-dark)", + }} + interface="popover" + > + All Templates + {getAvailableTemplates().map((template) => ( + + {template.template} + + ))} + +
+
{ if (currentKey) { await props.store._deleteFile(currentKey); - loadDefault(); setCurrentKey(null); await renderFileList(); } diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 6df9084..f00667b 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -16,7 +16,7 @@ import { documents, key, } from "ionicons/icons"; -import { APP_NAME, DATA } from "../../app-data"; +import { APP_NAME, DATA } from "../../templates.js"; import { useTheme } from "../../contexts/ThemeContext"; import { useInvoice } from "../../contexts/InvoiceContext"; import { exportHTMLAsPDF } from "../../services/exportAsPdf.js"; @@ -56,9 +56,9 @@ const Menu: React.FC<{ /* Utility functions */ const _validateName = async (filename) => { filename = filename.trim(); - if (filename === "default" || filename === "Untitled") { + if (filename === "Untitled") { setToastMessage( - "cannot update default or Untitled file! Use Save As Button to save." + "cannot update Untitled file! Use Save As Button to save." ); return false; } else if (filename === "" || !filename) { @@ -338,6 +338,10 @@ const Menu: React.FC<{ // Get all sheets data using the new function from index.js const sheetsData = AppGeneral.getAllSheetsData(); + if (sheetsData.length > 3) { + console.log(sheetsData); + return; + } if (!sheetsData || sheetsData.length === 0) { setToastMessage("No sheets available to export"); diff --git a/src/components/Storage/LocalStorage.ts b/src/components/Storage/LocalStorage.ts index b84e0b5..aa8469a 100644 --- a/src/components/Storage/LocalStorage.ts +++ b/src/components/Storage/LocalStorage.ts @@ -1,6 +1,28 @@ import { Preferences } from "@capacitor/preferences"; import CryptoJS from "crypto-js"; +// Enhanced Template Metadata Interface +export interface TemplateMetadata { + template: string; + templateId: number; + footers: { + name: string; + index: number; + isActive: boolean; + }[]; + logoCell: string | null; + signatureCell: string | null; + cellMappings: { + [headingName: string]: { + [cellName: string]: { + heading: string; + datatype: string; + }; + }; + }; +} + +// Enhanced File class with template metadata export class File { created: string; modified: string; @@ -9,6 +31,7 @@ export class File { billType: number; isEncrypted: boolean; password?: string; + templateMetadata: TemplateMetadata; constructor( created: string, @@ -16,7 +39,8 @@ export class File { content: string, name: string, billType: number, - isEncrypted: boolean = false, + templateMetadataOrIsEncrypted?: TemplateMetadata | boolean, + isEncryptedOrPassword?: boolean | string, password?: string ) { this.created = created; @@ -24,8 +48,35 @@ export class File { this.content = content; this.name = name; this.billType = billType; - this.isEncrypted = isEncrypted; - this.password = password; + + // Handle backward compatibility + if (typeof templateMetadataOrIsEncrypted === "boolean") { + // Old constructor signature: (created, modified, content, name, billType, isEncrypted, password) + this.isEncrypted = templateMetadataOrIsEncrypted; + this.password = isEncryptedOrPassword as string; + + // Create default template metadata + this.templateMetadata = { + template: `Template ${billType}`, + templateId: billType, + footers: [], + logoCell: null, + signatureCell: null, + cellMappings: {}, + }; + } else { + // New constructor signature: (created, modified, content, name, billType, templateMetadata, isEncrypted, password) + this.templateMetadata = templateMetadataOrIsEncrypted || { + template: `Template ${billType}`, + templateId: billType, + footers: [], + logoCell: null, + signatureCell: null, + cellMappings: {}, + }; + this.isEncrypted = (isEncryptedOrPassword as boolean) || false; + this.password = password; + } } } @@ -52,6 +103,7 @@ export class Local { name: file.name, billType: file.billType, isEncrypted: file.isEncrypted, + templateMetadata: file.templateMetadata, }; // If file is password protected, encrypt the content @@ -99,6 +151,7 @@ export class Local { created: (data as any).created, modified: (data as any).modified, isEncrypted: (data as any).isEncrypted || false, + templateMetadata: (data as any).templateMetadata || null, }; } return arr; @@ -126,4 +179,67 @@ export class Local { return false; } }; + + // Get files by template ID + _getFilesByTemplate = async (templateId: number) => { + const allFiles = await this._getAllFiles(); + const templateFiles = {}; + + for (const [fileName, fileInfo] of Object.entries(allFiles)) { + if ((fileInfo as any).templateMetadata?.templateId === templateId) { + templateFiles[fileName] = fileInfo; + } + } + + return templateFiles; + }; + + // Get template metadata for a specific file + _getTemplateMetadata = async ( + fileName: string + ): Promise => { + try { + const data = await this._getFile(fileName); + return data.templateMetadata || null; + } catch (error) { + return null; + } + }; + + // Update template metadata for a file + _updateTemplateMetadata = async ( + fileName: string, + metadata: TemplateMetadata + ) => { + try { + const data = await this._getFile(fileName); + data.templateMetadata = metadata; + data.modified = new Date().toISOString(); + + await Preferences.set({ + key: fileName, + value: JSON.stringify(data), + }); + + return true; + } catch (error) { + console.error("Error updating template metadata:", error); + return false; + } + }; + + // Get unique template IDs from all files + _getAvailableTemplates = async () => { + const allFiles = await this._getAllFiles(); + const templateIds = new Set(); + + for (const fileInfo of Object.values(allFiles)) { + const templateId = (fileInfo as any).templateMetadata?.templateId; + if (templateId) { + templateIds.add(templateId); + } + } + + return Array.from(templateIds); + }; } diff --git a/src/components/TemplateFiles.tsx b/src/components/TemplateFiles.tsx new file mode 100644 index 0000000..6424870 --- /dev/null +++ b/src/components/TemplateFiles.tsx @@ -0,0 +1,264 @@ +import React, { useState, useEffect } from 'react'; +import { + IonCard, + IonCardContent, + IonCardHeader, + IonCardTitle, + IonButton, + IonSegment, + IonSegmentButton, + IonLabel, + IonList, + IonItem, + IonIcon, + IonBadge, + IonChip, + IonToast, +} from '@ionic/react'; +import { documentText, folder, download, create, trash } from 'ionicons/icons'; +import { Local, File, TemplateMetadata } from './Storage/LocalStorage'; +import { TemplateManager } from '../utils/templateManager'; +import { TemplateInitializer } from '../utils/templateInitializer'; + +interface TemplateFilesProps { + onFileSelect?: (fileName: string, templateId: number) => void; + onFileCreate?: (templateId: number) => void; +} + +/** + * Enhanced Files Component with Multi-Template Support + * Demonstrates the new template isolation architecture + */ +const TemplateFiles: React.FC = ({ onFileSelect, onFileCreate }) => { + const [selectedTemplate, setSelectedTemplate] = useState('all'); + const [files, setFiles] = useState>({}); + const [availableTemplates, setAvailableTemplates] = useState([]); + const [loading, setLoading] = useState(true); + const [toastMessage, setToastMessage] = useState(''); + + const local = new Local(); + + useEffect(() => { + loadFiles(); + loadAvailableTemplates(); + }, []); + + const loadFiles = async () => { + try { + setLoading(true); + const allFiles = await local._getAllFiles(); + setFiles(allFiles); + } catch (error) { + console.error('Error loading files:', error); + setToastMessage('Error loading files'); + } finally { + setLoading(false); + } + }; + + const loadAvailableTemplates = async () => { + try { + const allFiles = await local._getAllFiles(); + const templateIds = TemplateManager.getUniqueTemplateIds(allFiles); + setAvailableTemplates(templateIds); + } catch (error) { + console.error('Error loading templates:', error); + } + }; + + const getFilteredFiles = () => { + if (selectedTemplate === 'all') { + return files; + } + return TemplateManager.filterFilesByTemplate(files, selectedTemplate as number); + }; + + const getTemplateInfo = (templateId: number) => { + const metadata = TemplateInitializer.getTemplateMetadata(templateId); + return metadata ? metadata.template : `Template ${templateId}`; + }; + + const handleFileCreate = async (templateId: number) => { + try { + const metadata = TemplateInitializer.getTemplateMetadata(templateId); + if (!metadata) { + setToastMessage('Template not found'); + return; + } + + const mscContent = TemplateInitializer.createMSCContent(templateId); + if (!mscContent) { + setToastMessage('Error creating template content'); + return; + } + + const fileName = `invoice_${Date.now()}.msc`; + const newFile = new File( + new Date().toISOString(), + new Date().toISOString(), + mscContent, + fileName, + templateId, + metadata, + false + ); + + await local._saveFile(newFile); + await loadFiles(); + setToastMessage(`File created with ${metadata.template}`); + + if (onFileCreate) { + onFileCreate(templateId); + } + } catch (error) { + console.error('Error creating file:', error); + setToastMessage('Error creating file'); + } + }; + + const handleFileDelete = async (fileName: string) => { + try { + await local._deleteFile(fileName); + await loadFiles(); + setToastMessage('File deleted successfully'); + } catch (error) { + console.error('Error deleting file:', error); + setToastMessage('Error deleting file'); + } + }; + + const getFileTemplateInfo = (fileData: any): TemplateMetadata | null => { + return fileData.templateMetadata || null; + }; + + const filteredFiles = getFilteredFiles(); + const fileCount = Object.keys(filteredFiles).length; + + return ( +
+ + + Multi-Template File Manager + + + {/* Template Filter */} + setSelectedTemplate(e.detail.value as number | 'all')} + > + + All Templates + {Object.keys(files).length} + + {availableTemplates.map(templateId => ( + + {getTemplateInfo(templateId)} + + {Object.keys(TemplateManager.filterFilesByTemplate(files, templateId)).length} + + + ))} + + + {/* Create New File Buttons */} +
+ {TemplateInitializer.getAllTemplates().map(template => ( + handleFileCreate(template.templateId)} + > + + New {template.template} + + ))} +
+ + {/* Files List */} + {loading ? ( +
Loading files...
+ ) : fileCount === 0 ? ( +
+ +
No files found for selected template
+
+ ) : ( + + {Object.entries(filteredFiles).map(([fileName, fileData]) => { + const templateMetadata = getFileTemplateInfo(fileData); + return ( + + +
+
{fileName}
+
+ Created: {new Date(fileData.created).toLocaleDateString()} + {fileData.modified !== fileData.created && ( + <> • Modified: {new Date(fileData.modified).toLocaleDateString()} + )} +
+ {templateMetadata && ( +
+ + {templateMetadata.template} + + {templateMetadata.footers.length > 0 && ( + + {templateMetadata.footers.length} footer(s) + + )} + {fileData.isEncrypted && ( + + Encrypted + + )} +
+ )} +
+ onFileSelect && onFileSelect(fileName, templateMetadata?.templateId || 1)} + > + + + handleFileDelete(fileName)} + > + + +
+ ); + })} +
+ )} + + {/* Template Isolation Info */} + {selectedTemplate !== 'all' && ( +
+
+ Template Isolation Active +
+
+ Showing only files for {getTemplateInfo(selectedTemplate as number)}. + Files are isolated by template to prevent interference between different invoice types. +
+
+ )} +
+
+ + setToastMessage('')} + message={toastMessage} + duration={3000} + position="bottom" + /> +
+ ); +}; + +export default TemplateFiles; diff --git a/src/contexts/InvoiceContext.tsx b/src/contexts/InvoiceContext.tsx index 50b1d7b..0508fa4 100644 --- a/src/contexts/InvoiceContext.tsx +++ b/src/contexts/InvoiceContext.tsx @@ -11,8 +11,10 @@ interface InvoiceContextType { selectedFile: string; billType: number; store: Local; + activeTempId: number; updateSelectedFile: (fileName: string) => void; updateBillType: (type: number) => void; + updateActiveTempId: (tempId: number) => void; resetToDefaults: () => void; } @@ -35,6 +37,7 @@ export const InvoiceProvider: React.FC = ({ }) => { const [selectedFile, setSelectedFile] = useState("default"); const [billType, setBillType] = useState(1); + const [activeTempId, setActiveTempId] = useState(1); const [store] = useState(() => new Local()); // Load persisted state from localStorage on mount @@ -42,6 +45,7 @@ export const InvoiceProvider: React.FC = ({ try { const savedFile = localStorage.getItem("stark-invoice-selected-file"); const savedBillType = localStorage.getItem("stark-invoice-bill-type"); + const savedActiveTempId = localStorage.getItem("stark-invoice-active-temp-id"); if (savedFile) { setSelectedFile(savedFile); @@ -50,6 +54,10 @@ export const InvoiceProvider: React.FC = ({ if (savedBillType) { setBillType(parseInt(savedBillType, 10)); } + + if (savedActiveTempId) { + setActiveTempId(parseInt(savedActiveTempId, 10)); + } } catch (error) { console.warn("Failed to load invoice state from localStorage:", error); } @@ -72,6 +80,14 @@ export const InvoiceProvider: React.FC = ({ } }, [billType]); + useEffect(() => { + try { + localStorage.setItem("stark-invoice-active-temp-id", activeTempId.toString()); + } catch (error) { + console.warn("Failed to save active temp id to localStorage:", error); + } + }, [activeTempId]); + const updateSelectedFile = (fileName: string) => { setSelectedFile(fileName); }; @@ -80,17 +96,24 @@ export const InvoiceProvider: React.FC = ({ setBillType(type); }; + const updateActiveTempId = (tempId: number) => { + setActiveTempId(tempId); + }; + const resetToDefaults = () => { setSelectedFile("default"); setBillType(1); + setActiveTempId(1); }; const value: InvoiceContextType = { selectedFile, billType, store, + activeTempId, updateSelectedFile, updateBillType, + updateActiveTempId, resetToDefaults, }; diff --git a/src/pages/FilesPage.tsx b/src/pages/FilesPage.tsx index 92ed3ec..60a1a26 100644 --- a/src/pages/FilesPage.tsx +++ b/src/pages/FilesPage.tsx @@ -7,21 +7,32 @@ import { IonTitle, IonToast, IonToolbar, + IonButton, + IonIcon, + IonButtons, } from "@ionic/react"; +import { + chevronForward, + chevronUp, + chevronDown, + layers, + settings, +} from "ionicons/icons"; import Files from "../components/Files/Files"; import { useTheme } from "../contexts/ThemeContext"; import { useInvoice } from "../contexts/InvoiceContext"; -import { DATA } from "../app-data"; +import { DATA } from "../templates"; +import { tempMeta } from "../templates-meta"; import * as AppGeneral from "../components/socialcalc/index"; import "./FilesPage.css"; import { useHistory } from "react-router-dom"; import { File } from "../components/Storage/LocalStorage"; +import { TemplateInitializer } from "../utils/templateInitializer"; const FilesPage: React.FC = () => { const { isDarkMode } = useTheme(); const { selectedFile, - resetToDefaults, billType, store, updateSelectedFile, @@ -31,135 +42,99 @@ const FilesPage: React.FC = () => { const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); - const [showUnsavedChangesAlert, setShowUnsavedChangesAlert] = useState(false); + const [showAllTemplates, setShowAllTemplates] = useState(false); + const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); + const [selectedTemplateForFile, setSelectedTemplateForFile] = useState(null); + const [newFileName, setNewFileName] = useState(""); const [device] = useState(AppGeneral.getDeviceType()); - const handleNewFileClick = async () => { - try { - // Get the default file from storage - const defaultExists = await store._checkKey("default"); - if (selectedFile === "default" && defaultExists) { - const storedDefaultFile = await store._getFile("default"); - - // Decode the stored content - const storedContent = decodeURIComponent(storedDefaultFile.content); - const msc = DATA["home"]["App"]["msc"]; - - const hasUnsavedChanges = storedContent !== JSON.stringify(msc); - - if (hasUnsavedChanges) { - // If there are unsaved changes, show confirmation alert - setShowUnsavedChangesAlert(true); - return; - } - } - await createNewFile(); - } catch (error) { - console.error("Error checking for unsaved changes:", error); - // On error, proceed with normal flow - setShowUnsavedChangesAlert(true); - } + // Template helper functions + const getAvailableTemplates = () => { + return TemplateInitializer.getAllTemplates(); }; - const handleNewMedClick = async () => { - try { - // Get the default file from storage - const defaultExists = await store._checkKey("default"); - if (selectedFile === "default" && defaultExists) { - const storedDefaultFile = await store._getFile("default"); - // Decode the stored content - const storedContent = decodeURIComponent(storedDefaultFile.content); - const msc = DATA["home"]["App"]["msc"]; + const getTemplateInfo = (templateId: number) => { + const template = TemplateInitializer.getTemplate(templateId); + return template ? template.template : `Template ${templateId}`; + }; - const hasUnsavedChanges = storedContent !== JSON.stringify(msc); + const getTemplateMetadata = (templateId: number) => { + return tempMeta.find(meta => meta.template_id === templateId); + }; - if (hasUnsavedChanges) { - // If there are unsaved changes, show confirmation alert - setShowUnsavedChangesAlert(true); - return; - } - } - await createNewMed(); - } catch (error) { - console.error("Error checking for unsaved changes:", error); - // On error, proceed with normal flow - setShowUnsavedChangesAlert(true); - } + const handleTemplateSelect = (templateId: number) => { + setSelectedTemplateForFile(templateId); + setShowFileNamePrompt(true); }; - const createNewFile = async () => { + // Create new file with template + const createNewFileWithTemplate = async (templateId: number, fileName: string) => { try { - // Reset to defaults first - resetToDefaults(); - - // Set selected file to "default" - updateSelectedFile("default"); - - const msc = DATA["home"]["App"]["msc"]; + const metadata = TemplateInitializer.getTemplateMetadata(templateId); + if (!metadata) { + setToastMessage("Template not found"); + setShowToast(true); + return; + } - // Load the template data into the spreadsheet - AppGeneral.viewFile("default", JSON.stringify(msc)); + const mscContent = TemplateInitializer.createMSCContent(templateId); + if (!mscContent) { + setToastMessage("Error creating template content"); + setShowToast(true); + return; + } - // Save the new template as the default file in storage - const templateContent = encodeURIComponent(JSON.stringify(msc)); const now = new Date().toISOString(); - const newDefaultFile = new File(now, now, templateContent, "default", 1); - await store._saveFile(newDefaultFile); + const newFile = new File( + now, + now, + encodeURIComponent(mscContent), // mscContent is already a JSON string + fileName, + templateId, + metadata, + false + ); - setToastMessage("New file created successfully"); - setShowToast(true); - history.push("/app/editor"); - } catch (error) { - console.error("Error creating new file:", error); - setToastMessage("Error creating new invoice"); + await store._saveFile(newFile); + + setToastMessage(`File "${fileName}" created with ${metadata.template}`); setShowToast(true); - } - }; - - const createNewMed = async () => { - try { - // Reset to defaults first - resetToDefaults(); - console.log("creating new Med"); - // Set selected file to "default" - updateSelectedFile("default"); - - const msc = DATA["home"]["Medication"]["msc"]; - // Save the new template as the default file in storage - const templateContent = encodeURIComponent(JSON.stringify(msc)); - const now = new Date().toISOString(); - const newDefaultFile = new File(now, now, templateContent, "default", 1); - await store._saveFile(newDefaultFile); - - // Load the template data into the spreadsheet - // AppGeneral.viewFile("default", JSON.stringify(msc)); + + // Reset modal state + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + setNewFileName(""); - setToastMessage("New file created successfully"); - setShowToast(true); - // Reset to defaults first - resetToDefaults(); - history.push("/app/editor"); + // Navigate to editor with the new file using the new URL structure + updateSelectedFile(fileName); + updateBillType(templateId); + + // Don't initialize SocialCalc here - let the Home page handle initialization + // when it loads with the selected file + + history.push(`/app/editor/${encodeURIComponent(fileName)}`); } catch (error) { - console.error("Error creating new file:", error); - setToastMessage("Error creating new invoice"); + console.error("Error creating file:", error); + setToastMessage("Failed to create file"); setShowToast(true); } }; - const handleDiscardAndCreateNew = async () => { - try { - // User confirmed to discard changes, proceed with creating new file - await createNewFile(); - setShowUnsavedChangesAlert(false); - } catch (error) { - console.error("Error discarding and creating new file:", error); - setToastMessage("Error creating new invoice"); - setShowToast(true); - setShowUnsavedChangesAlert(false); - } + const handleNewFileClick = async () => { + // Directly show the template selection modal + // No need to check for unsaved changes since we removed the default file + setShowAllTemplates(false); + }; + const handleNewMedClick = async () => { + // Directly show the template selection modal + // No need to check for unsaved changes since we removed the default file + setShowAllTemplates(false); }; + // Removed createNewFile and createNewMed functions since they handled default file logic + // Now we only use createNewFileWithTemplate for creating files with specific templates + return ( @@ -174,21 +149,151 @@ const FilesPage: React.FC = () => { > 🧾 Invoice App + + history.push("/app/settings")} + style={{ fontSize: "1.2em" }} + > + + + - {/* Template Creation Options */} -
-
-
-
🧾
-
Create New Invoice
-
-
-
💊
-
Medication Invoice
-
+ {/* Template Creation Section with Template Cards */} +
+

+ Create New File +

+ + {/* Template Cards - Show first 3, then expand button if more */} +
+ {getAvailableTemplates() + .slice(0, showAllTemplates ? undefined : 3) + .map((template) => { + const metadata = getTemplateMetadata(template.templateId); + return ( +
handleTemplateSelect(template.templateId)} + style={{ + border: "2px solid var(--ion-color-light)", + borderRadius: "12px", + padding: "20px", + cursor: "pointer", + transition: "all 0.3s ease", + backgroundColor: "var(--ion-color-light-tint)", + display: "flex", + alignItems: "center", + gap: "16px", + boxShadow: "0 2px 8px rgba(0,0,0,0.1)" + }} + onMouseOver={(e) => { + e.currentTarget.style.borderColor = "var(--ion-color-primary)"; + e.currentTarget.style.transform = "translateY(-4px)"; + e.currentTarget.style.boxShadow = "0 8px 24px rgba(0,0,0,0.15)"; + }} + onMouseOut={(e) => { + e.currentTarget.style.borderColor = "var(--ion-color-light)"; + e.currentTarget.style.transform = "translateY(0)"; + e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.1)"; + }} + > + {/* Template Image */} +
+ {metadata?.ImageUri ? ( + {metadata.name} + ) : ( + + )} +
+ + {/* Template Info */} +
+

+ {metadata?.name || template.template} +

+

+ {template.footers.length} footer(s) +

+
+ + {/* Arrow Icon */} + +
+ ); + })}
+ + {/* Show More Templates Button */} + {getAvailableTemplates().length > 3 && ( +
+ setShowAllTemplates(!showAllTemplates)} + style={{ margin: "0 auto" }} + > + + {showAllTemplates ? 'Show Less' : `View ${getAvailableTemplates().length - 3} More Templates`} + +
+ )}
{ updateBillType={updateBillType} /> - {/* unsaved changes alert */} + + setShowToast(false)} + message={toastMessage} + duration={3000} + color={toastMessage.includes("successfully") ? "success" : "warning"} + position="top" + /> + + {/* File Name Prompt Alert */} setShowUnsavedChangesAlert(false)} - header="⚠️ Unsaved Changes" - message="The default file has unsaved changes. Creating a new file will discard these changes. Do you want to continue?" + animated + isOpen={showFileNamePrompt} + onDidDismiss={() => { + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + setNewFileName(""); + }} + header="Create New File" + message={ + selectedTemplateForFile && getTemplateMetadata(selectedTemplateForFile) + ? `Create a new ${getTemplateMetadata(selectedTemplateForFile)?.name} file` + : 'Create a new invoice file' + } + inputs={[ + { + name: "filename", + type: "text", + value: newFileName, + placeholder: "Enter file name", + }, + ]} buttons={[ { text: "Cancel", role: "cancel", handler: () => { - setShowUnsavedChangesAlert(false); + setSelectedTemplateForFile(null); + setNewFileName(""); }, }, { - text: "Discard & Create New", - handler: async () => { - await handleDiscardAndCreateNew(); + text: "Create", + handler: (data) => { + const fileName = data.filename?.trim(); + if (fileName && selectedTemplateForFile) { + createNewFileWithTemplate(selectedTemplateForFile, fileName); + } else { + setToastMessage("Please enter a file name"); + setShowToast(true); + } }, }, ]} /> - setShowToast(false)} - message={toastMessage} - duration={3000} - color={toastMessage.includes("successfully") ? "success" : "warning"} - position="top" - /> ); }; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 548cf12..0eb426d 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -24,7 +24,7 @@ import { IonFabButton, isPlatform, } from "@ionic/react"; -import { APP_NAME, DATA } from "../app-data"; +import { APP_NAME, DATA } from "../templates"; import * as AppGeneral from "../components/socialcalc/index.js"; import { useEffect, useState, useRef } from "react"; import { Local, File } from "../components/Storage/LocalStorage"; @@ -43,6 +43,9 @@ import { downloadOutline, createOutline, refreshOutline, + arrowBack, + documentText, + folder, } from "ionicons/icons"; import "./Home.css"; import FileOptions from "../components/FileMenu/FileOptions"; @@ -51,6 +54,7 @@ import PWAInstallPrompt from "../components/PWAInstallPrompt"; import { usePWA } from "../hooks/usePWA"; import { useTheme } from "../contexts/ThemeContext"; import { useInvoice } from "../contexts/InvoiceContext"; +import { useHistory, useParams } from "react-router-dom"; import InvoiceForm from "../components/InvoiceForm"; // import WalletConnection from "../components/wallet/WalletConnection"; import { @@ -59,13 +63,19 @@ import { isQuotaExceededError, getQuotaExceededMessage, } from "../utils/helper"; +import { TemplateInitializer } from "../utils/templateInitializer"; +import { TemplateManager } from "../utils/templateManager"; // import { cloudService } from "../services/cloud-service"; const Home: React.FC = () => { const { isDarkMode } = useTheme(); - const { selectedFile, billType, store, updateSelectedFile, updateBillType } = + const { selectedFile, billType, store, updateSelectedFile, updateBillType, activeTempId, updateActiveTempId } = useInvoice(); const { isInstallable, isInstalled, isOnline, installApp } = usePWA(); + const history = useHistory(); + const { fileName } = useParams<{ fileName?: string }>(); + + const [fileNotFound, setFileNotFound] = useState(false); const [showMenu, setShowMenu] = useState(false); const [device] = useState(AppGeneral.getDeviceType()); @@ -199,7 +209,26 @@ const Home: React.FC = () => { try { const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); const now = new Date().toISOString(); - const file = new File(now, now, content, fileName, billType); + + // Get template metadata for the current active template + const metadata = TemplateInitializer.getTemplateMetadata(activeTempId); + + const file = new File( + now, + now, + content, + fileName, + billType, + metadata || { + template: `Template ${activeTempId}`, + templateId: activeTempId, + footers: [], + logoCell: null, + signatureCell: null, + cellMappings: {}, + }, + false + ); await store._saveFile(file); setToastMessage(`File "${fileName}" saved locally!`); @@ -222,82 +251,59 @@ const Home: React.FC = () => { useEffect(() => { const initializeApp = async () => { try { - // First try to load the default file from local storage - const defaultExists = await store._checkKey("default"); - if (defaultExists) { - const defaultFile = await store._getFile("default"); - const decodedContent = decodeURIComponent(defaultFile.content); - - AppGeneral.viewFile("default", decodedContent); - updateBillType(defaultFile.billType); - console.log("Loaded existing default file from local storage"); - } else { - // If no default file exists, initialize with template data and save it - const data = DATA["home"]["App"]["msc"]; - AppGeneral.initializeApp(JSON.stringify(data)); - - // Save the initial template as the default file - const initialContent = encodeURIComponent(JSON.stringify(data)); - const now = new Date().toISOString(); - const file = new File(now, now, initialContent, "default", billType); - await store._saveFile(file); - console.log("Created and saved new default file"); + // Initialize template system first + const isTemplateInitialized = await TemplateInitializer.isInitialized(); + if (!isTemplateInitialized) { + await TemplateInitializer.initializeApp(); } - } catch (error) { - console.error("Error initializing app:", error); - // Check if the error is due to storage quota exceeded - if (isQuotaExceededError(error)) { - setToastMessage(getQuotaExceededMessage("initializing the app")); - setToastColor("danger"); - setShowToast(true); + // Determine which file to load + let fileToLoad = fileName || selectedFile; + + // If no file is specified in URL or context, redirect to files page + if (!fileToLoad) { + console.log("No file specified, redirecting to files"); + history.push("/app/files"); + return; } - const data = DATA["home"]["App"]["msc"]; - AppGeneral.initializeApp(JSON.stringify(data)); - AppGeneral.changeSheetColor("#000000"); - } - // Alternative smooth scrolling implementation - setTimeout(() => { - const gridDiv = document.getElementById("te_griddiv"); - if (gridDiv) { - // Force smooth scrolling CSS - gridDiv.style.scrollBehavior = "smooth"; - - // Override SocialCalc's internal scrolling - const tables = gridDiv.querySelectorAll("table"); - tables.forEach((table) => { - table.style.scrollBehavior = "smooth"; - }); - - // Intercept keyboard navigation to use smooth scrolling - gridDiv.addEventListener("keydown", function (e) { - const step = 30; // Pixels to scroll per key press - - switch (e.key) { - case "ArrowUp": - e.preventDefault(); - gridDiv.scrollBy({ top: -step, behavior: "smooth" }); - break; - case "ArrowDown": - e.preventDefault(); - gridDiv.scrollBy({ top: step, behavior: "smooth" }); - break; - case "ArrowLeft": - e.preventDefault(); - gridDiv.scrollBy({ left: -step, behavior: "smooth" }); - break; - case "ArrowRight": - e.preventDefault(); - gridDiv.scrollBy({ left: step, behavior: "smooth" }); - break; - } - }); + + // Check if the file exists in storage + const fileExists = await store._checkKey(fileToLoad); + if (!fileExists) { + console.log(`File "${fileToLoad}" not found`); + setFileNotFound(true); + return; } - }, 1000); + + // Load the file + const fileData = await store._getFile(fileToLoad); + const decodedContent = decodeURIComponent(fileData.content); + + // Update context if URL parameter is different from selected file + if (fileName && fileName !== selectedFile) { + updateSelectedFile(fileName); + } + + // Use initializeApp instead of viewFile to ensure proper SocialCalc setup + AppGeneral.initializeApp(decodedContent); + updateBillType(fileData.billType); + + // Update active template if file has template metadata + if (fileData.templateMetadata?.templateId) { + updateActiveTempId(fileData.templateMetadata.templateId); + } + + console.log("Loaded file:", fileToLoad); + setFileNotFound(false); + } catch (error) { + console.error("Error initializing app:", error); + // On error, show file not found + setFileNotFound(true); + } }; initializeApp(); - }, []); + }, [fileName, selectedFile]); const [autoSaveTimer, setAutoSaveTimer] = useState( null @@ -306,24 +312,24 @@ const Home: React.FC = () => { const handleAutoSave = async () => { try { console.log("Auto-saving file..."); - const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); - - if (selectedFile === "default") { - // Autosave the default file to local storage - const now = new Date().toISOString(); - const file = new File(now, now, content, "default", billType); - await store._saveFile(file); + + // If no file is selected, can't autosave + if (!selectedFile) { return; } - // For named files, get existing metadata and update + const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); + + // Get existing metadata and update const data = await store._getFile(selectedFile); const file = new File( (data as any)?.created || new Date().toISOString(), new Date().toISOString(), content, selectedFile, - billType + billType, + (data as any)?.templateMetadata, + false ); await store._saveFile(file); updateSelectedFile(selectedFile); @@ -407,7 +413,7 @@ const Home: React.FC = () => { } }, [isDarkMode, activeFontColor]); - const footers = DATA["home"]["App"]["footers"]; + const footers = DATA[activeTempId]["footers"]; const footersList = footers.map((footerArray) => { const isActive = footerArray.index === billType; @@ -441,7 +447,16 @@ const Home: React.FC = () => { > - + + history.push("/app/files")} + style={{ color: "white" }} + > + + + +
{ ) : ( {selectedFile} )} - {selectedFile !== "default" && ( + {selectedFile && ( { -
-
-
-
-
+ {fileNotFound ? ( +
+ +

+ File Not Found +

+

+ {fileName ? `The file "${fileName}" doesn't exist in your storage.` : "The requested file couldn't be found."} +

+ history.push("/app/files")} + style={{ minWidth: "200px" }} + > + + Go to File Explorer + +
+ ) : ( +
+
+
+
+
+ )} {/* Toast for save notifications */} { const [showMenu, setShowMenu] = useState(false); const { isDarkMode, toggleDarkMode } = useTheme(); + const history = useHistory(); const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); const [isLoading, setIsLoading] = useState(false); @@ -859,6 +862,14 @@ const SettingsPage: React.FC = () => { > + + history.push("/app/files")} + > + + + Settings diff --git a/src/templates-meta.ts b/src/templates-meta.ts new file mode 100644 index 0000000..090c4ce --- /dev/null +++ b/src/templates-meta.ts @@ -0,0 +1,14 @@ +export let tempMeta = [ + { + name: "Mobile Invoice 1", + template_id: 1, + ImageUri: + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + }, + { + name: "Mobile Invoice 2", + template_id: 2, + ImageUri: + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + }, +]; diff --git a/src/templates-new.ts b/src/templates-new.ts new file mode 100644 index 0000000..711cc65 --- /dev/null +++ b/src/templates-new.ts @@ -0,0 +1,189 @@ +export let APP_NAME = "Invoice Suite"; + +// Template Interface for better type safety +export interface TemplateData { + template: string; + templateId: number; + msc: { + numsheets: number; + currentid: string; + currentname: string; + sheetArr: { + [sheetName: string]: { + sheetstr: { + savestr: string; + }; + name: string; + hidden: string; + }; + }; + EditableCells: { + allow: boolean; + cells: { + [cellName: string]: boolean; + }; + }; + Prompts: { + [cellName: string]: [string, string, string, string]; + }; + }; + footers: { + name: string; + index: number; + isActive: boolean; + }[]; + logoCell: string | null; + signatureCell: string | null; + cellMappings: { + [headingName: string]: { + [cellName: string]: { + heading: string; + datatype: string; + }; + }; + }; +} + +// Sample template metadata for demonstration +export const TEMPLATE_METADATA_SAMPLES: { + [key: number]: Pick< + TemplateData, + "footers" | "logoCell" | "signatureCell" | "cellMappings" + >; +} = { + 1: { + footers: [ + { name: "Detail1", index: 1, isActive: false }, + { name: "Detail2", index: 2, isActive: false }, + { name: "Invoice", index: 3, isActive: true }, + ], + logoCell: "F8", + signatureCell: null, + cellMappings: { + "Company Information": { + B8: { heading: "Company Name", datatype: "text" }, + B9: { heading: "Street Address", datatype: "text" }, + B10: { heading: "City, State, Zip", datatype: "text" }, + B11: { heading: "Phone", datatype: "text" }, + B12: { heading: "Email", datatype: "email" }, + }, + "Bill To": { + B15: { heading: "Customer Name", datatype: "text" }, + B16: { heading: "Customer Company", datatype: "text" }, + B17: { heading: "Customer Address", datatype: "text" }, + B18: { heading: "Customer City, State, Zip", datatype: "text" }, + B19: { heading: "Customer Phone", datatype: "text" }, + B20: { heading: "Customer Email", datatype: "email" }, + }, + "Line Items": { + B23: { heading: "Description", datatype: "text" }, + G23: { heading: "Amount", datatype: "decimal" }, + }, + }, + }, + 2: { + footers: [ + { name: "Invoice 1", index: 1, isActive: true }, + { name: "Invoice 2", index: 2, isActive: false }, + ], + logoCell: null, + signatureCell: null, + cellMappings: { + Header: { + B2: { heading: "Invoice Title", datatype: "text" }, + B5: { heading: "Invoice Number", datatype: "text" }, + F4: { heading: "Date", datatype: "date" }, + }, + "Line Items": { + C23: { heading: "Description", datatype: "text" }, + E23: { heading: "Quantity", datatype: "number" }, + F23: { heading: "Price", datatype: "decimal" }, + }, + }, + }, +}; + +// Simplified template data structure +export let DATA: { [key: number]: TemplateData } = { + 1: { + template: "Mobile Invoice 1", + templateId: 1, + msc: { + numsheets: 3, + currentid: "sheet3", + currentname: "sheet6", + sheetArr: { + // Existing sheet data would go here - keeping original structure + // For brevity, using a simplified version here + }, + EditableCells: { + allow: true, + cells: { + "sheet6!B2": true, + "sheet6!F4": true, + "sheet6!G4": true, + // ... other editable cells + }, + }, + Prompts: { + "sheet6!B2": ["prompttext", "0", "1e10", "Invoice"], + "sheet6!F4": ["prompttext", "0", "1e10", "Date"], + "sheet6!G4": ["prompttext", "0", "1e10", "Date"], + // ... other prompts + }, + }, + ...TEMPLATE_METADATA_SAMPLES[1], + }, + 2: { + template: "Mobile Invoice 2", + templateId: 2, + msc: { + numsheets: 2, + currentid: "sheet1", + currentname: "inv1", + sheetArr: { + // Existing sheet data would go here + }, + EditableCells: { + allow: true, + cells: { + "inv1!B2": true, + "inv1!C5": true, + // ... other editable cells + }, + }, + Prompts: { + "inv1!B2": ["prompttext", "0", "1e10", "Invoice"], + "inv1!C5": ["prompttext", "0", "1e10", "From"], + // ... other prompts + }, + }, + ...TEMPLATE_METADATA_SAMPLES[2], + }, +}; + +// Helper functions for template management +export const getTemplateMetadata = (templateId: number) => { + const template = DATA[templateId]; + if (!template) return null; + + return { + template: template.template, + templateId: template.templateId, + footers: template.footers, + logoCell: template.logoCell, + signatureCell: template.signatureCell, + cellMappings: template.cellMappings, + }; +}; + +export const getAvailableTemplates = () => { + return Object.keys(DATA).map((id) => ({ + id: parseInt(id), + name: DATA[parseInt(id)].template, + })); +}; + +export const getTemplateById = (templateId: number) => { + return DATA[templateId] || null; +}; diff --git a/src/templates.ts b/src/templates.ts new file mode 100644 index 0000000..ef9714b --- /dev/null +++ b/src/templates.ts @@ -0,0 +1,546 @@ +export let APP_NAME = "Invoice Suite"; + +// Template Interface for better type safety +export interface TemplateData { + template: string; + templateId: number; + msc: { + numsheets: number; + currentid: string; + currentname: string; + sheetArr: { + [sheetName: string]: { + sheetstr: { + savestr: string; + }; + name: string; + hidden: string; + }; + }; + EditableCells: { + allow: boolean; + cells: { + [cellName: string]: boolean; + }; + constraints: { + [cellName: string]: [string, string, string, string]; + }; + }; + }; + footers: { + name: string; + index: number; + isActive: boolean; + }[]; + logoCell: string | null; + signatureCell: string | null; + cellMappings: { + [headingName: string]: { + [cellName: string]: { + heading: string; + datatype: string; + }; + }; + }; +} + +export let DATA: { [key: number]: TemplateData } = { + 1: { + template: "Mobile Invoice 1", + templateId: 1, + msc: { + numsheets: 3, + currentid: "sheet3", + currentname: "sheet6", + sheetArr: { + sheet1: { + sheetstr: { + savestr: + "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Hours:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Rate:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + }, + name: "inv3", + hidden: "0", + }, + sheet2: { + sheetstr: { + savestr: + "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Qty.:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Price:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + }, + name: "sheet7", + hidden: "0", + }, + sheet3: { + sheetstr: { + savestr: + 'version:1.5\ncell:B2:t:INVOICE:l:1:f:12:cf:1:colspan:6\ncell:C2:t::l:2:f:10\ncell:D2:t::l:2:f:10\ncell:E2:l:1:f:8:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:10\ncell:B3:f:5:cf:2:colspan:4\ncell:C3:t::l:2:f:10\ncell:D3:t::l:2:f:10\ncell:F3:l:1:f:6:cf:2\ncell:G3:l:1:f:11:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:11:cf:2\ncell:G4:vtf:nd:45892:TODAY():l:1:f:11:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:9:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:6:cf:2:colspan:2\ncell:G5:l:1:f:11:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:6:cf:2:colspan:2\ncell:G6:l:1:f:11:cf:2\ncell:B7:t:FROM\\c:f:11\ncell:F7:l:1:f:6:cf:2:colspan:2\ncell:G7:l:1:f:11:cf:2\ncell:B8:t:[Company Name]:f:9:colspan:4\ncell:F8:l:1:f:6:cf:2:tvf:4:colspan:2:rowspan:5\ncell:G8:l:1:f:11:cf:2\ncell:B9:t:[Street Address]:f:9:cf:2:colspan:4\ncell:F9:l:1:f:6\ncell:G9:l:1:f:11:cf:1\ncell:B10:t:[City, State, Zip]:f:9:cf:2:colspan:4\ncell:G10:l:1:f:10\ncell:B11:t:Phone\\c :f:9:cf:2:colspan:4\ncell:B12:t:Email\\c:f:9:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:9:cf:2\ncell:B15:t:[Name]:f:9:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:9:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:9:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:9:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:9:cf:2:colspan:6\ncell:B20:t:Email\\c:f:9:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:11:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:10\ncell:D22:t::l:2:f:10\ncell:E22:t::l:2:f:10\ncell:F22:t::l:2:f:10\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:11:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), sheet7!B6, inv3!B6)):b:1:1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:3\ncell:D23:t::l:2:f:3\ncell:E23:t::l:2:f:3\ncell:F23:t::b::2:::l:1:f:3\ncell:G23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), (sheet7!E6*sheet7!F6), (inv3!E6*inv3!F6))):b::1::1:f:3:ntvf:1\ncell:A24:b::1::\ncell:B24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), SHEET7!B7, INV3!B7)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:3\ncell:D24:t::l:2:f:3\ncell:E24:t::l:2:f:3\ncell:F24:t::b::2:::l:1:f:3\ncell:G24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), (SHEET7!E7*SHEET7!F7), (INV3!E7*INV3!F7))):b::1::1:f:3:ntvf:1\ncell:A25:b::1::\ncell:B25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), SHEET7!B8, INV3!B8)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:3\ncell:D25:t::l:2:f:3\ncell:E25:t::l:2:f:3\ncell:F25:t::b::2:::l:1:f:3\ncell:G25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), (SHEET7!E8*SHEET7!F8), (INV3!E8*INV3!F8))):b::1::1:f:3:ntvf:1\ncell:A26:b::1::\ncell:B26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), SHEET7!B9, INV3!B9)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:3\ncell:D26:t::l:2:f:3\ncell:E26:t::l:2:f:3\ncell:F26:t::b::2:::l:1:f:3\ncell:G26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), (SHEET7!E9*SHEET7!F9), (INV3!E9*INV3!F9))):b::1::1:f:3:ntvf:1\ncell:A27:b::1::\ncell:B27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), SHEET7!B10, INV3!B10)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:3\ncell:D27:t::l:2:f:3\ncell:E27:t::l:2:f:3\ncell:F27:t::b::2:::l:1:f:3\ncell:G27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), (SHEET7!E10*SHEET7!F10), (INV3!E10*INV3!F10))):b::1::1:f:3:ntvf:1\ncell:A28:b::1::\ncell:B28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), SHEET7!B11, INV3!B11)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:3\ncell:D28:t::l:2:f:3\ncell:E28:t::l:2:f:3\ncell:F28:t::b::2:::l:1:f:3\ncell:G28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), (SHEET7!E11*SHEET7!F11), (INV3!E11*INV3!F11))):b::1::1:f:3:ntvf:1\ncell:A29:b::1::\ncell:B29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), SHEET7!B12, INV3!B12)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:3\ncell:D29:t::l:2:f:3\ncell:E29:t::l:2:f:3\ncell:F29:t::b::2:::l:1:f:3\ncell:G29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), (SHEET7!E12*SHEET7!F12), (INV3!E12*INV3!F12))):b::1::1:f:3:ntvf:1\ncell:A30:b::1::\ncell:B30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), SHEET7!B13, INV3!B13)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:3\ncell:D30:t::l:2:f:3\ncell:E30:t::l:2:f:3\ncell:F30:t::b::2:::l:1:f:3\ncell:G30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), (SHEET7!E13*SHEET7!F13), (INV3!E13*INV3!F13))):b::1::1:f:3:ntvf:1\ncell:A31:b::1::\ncell:B31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), SHEET7!B14, INV3!B14)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:3\ncell:D31:t::l:2:f:3\ncell:E31:t::l:2:f:3\ncell:F31:t::b::2:::l:1:f:3\ncell:G31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), (SHEET7!E14*SHEET7!F14), (INV3!E14*INV3!F14))):b::1::1:f:3:ntvf:1\ncell:A32:b::1::\ncell:B32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), SHEET7!B15, INV3!B15)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:3\ncell:D32:t::l:2:f:3\ncell:E32:t::l:2:f:3\ncell:F32:t::b::2:::l:1:f:3\ncell:G32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), (SHEET7!E15*SHEET7!F15), (INV3!E15*INV3!F15))):b::1::1:f:3:ntvf:1\ncell:A33:b::1::\ncell:B33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), SHEET7!B16, INV3!B16)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C33:l:2:f:3\ncell:D33:l:2:f:3\ncell:E33:l:2:f:3\ncell:F33:b::2:::l:1:f:3\ncell:G33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), (SHEET7!E16*SHEET7!F16), (INV3!E16*INV3!F16))):b::1::1:f:3:ntvf:1\ncell:A34:b::1::\ncell:B34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), SHEET7!B17, INV3!B17)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C34:t::l:2:f:3\ncell:D34:t::l:2:f:3\ncell:E34:t::l:2:f:3\ncell:F34:t::b::2:::l:1:f:3\ncell:G34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), (SHEET7!E17*SHEET7!F17), (INV3!E17*INV3!F17))):b::1::1:f:3:ntvf:1\ncell:A35:b::1::\ncell:B35:vtf:t: :IF(AND(ISBLANK(INV3!B18), ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), SHEET7!B18, INV3!B18)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:3\ncell:D35:t::b:::2::l:1:f:3\ncell:E35:t::b:::2::l:1:f:3\ncell:F35:t::b::2:2::l:1:f:3\ncell:G35:vtf:t: :IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), (SHEET7!E18*SHEET7!F18), (INV3!E18*INV3!F18))):b::1::1:f:3:ntvf:1\ncell:B36:b:2::::l:1:f:10\ncell:C36:b:2::::l:1:f:10\ncell:D36:b:2::::l:1:f:10\ncell:E36:b:2::::l:1:f:11\ncell:F36:t:Subtotal:b:2::::l:1:f:9\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:9:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:11:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:10\ncell:D37:t::b:::2::l:1:f:10\ncell:F37:t:Tax Rate:l:1:f:9\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:10\ncell:D38:t::b:2::::l:1:f:10\ncell:F38:t:Tax:l:1:f:9\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:10\ncell:D39:t::l:2:f:10\ncell:F39:t:Other:b:::1::l:1:f:9\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:10\ncell:D40:t::l:2:f:10\ncell:F40:t:TOTAL:b:1::::l:1:f:11\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:9:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:4:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:10\ncell:D43:t::l:2:f:10\ncell:E43:t::l:2:f:10\ncell:F43:t::l:2:f:10\ncell:G43:t::l:2:f:10\ncol:A:w:10\ncol:B:w:65\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:53\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 9pt Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:italic normal * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 14pt Trebuchet MS\nfont:8:normal bold 28pt Trebuchet MS\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 10pt Trebuchet MS\nfont:12:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', + }, + name: "sheet6", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "sheet6!B2": true, + "sheet6!F4": true, + "sheet6!G4": true, + "sheet6!B5": true, + "sheet6!B7": true, + "sheet6!B8": true, + "sheet6!B9": true, + "sheet6!B10": true, + "sheet6!B11": true, + "sheet6!B12": true, + "sheet6!B14": true, + "sheet6!B15": true, + "sheet6!B16": true, + "sheet6!B17": true, + "sheet6!B18": true, + "sheet6!B19": true, + "sheet6!B20": true, + "sheet6!B38": true, + "sheet6!B39": true, + "sheet6!B40": true, + "sheet6!F36": true, + "sheet6!G37": true, + "sheet6!F37": true, + "sheet6!F39": true, + "sheet6!G39": true, + "sheet6!F38": true, + "inv3!B2": true, + "inv3!B6": true, + "inv3!B7": true, + "inv3!B8": true, + "inv3!B9": true, + "inv3!B10": true, + "inv3!B11": true, + "inv3!B12": true, + "inv3!B13": true, + "inv3!B14": true, + "inv3!B15": true, + "inv3!B16": true, + "inv3!B17": true, + "inv3!B18": true, + "inv3!E6": true, + "inv3!E7": true, + "inv3!E8": true, + "inv3!E9": true, + "inv3!E10": true, + "inv3!E11": true, + "inv3!E12": true, + "inv3!E13": true, + "inv3!E14": true, + "inv3!E15": true, + "inv3!E16": true, + "inv3!E17": true, + "inv3!E18": true, + "inv3!F6": true, + "inv3!F7": true, + "inv3!F8": true, + "inv3!F9": true, + "inv3!F10": true, + "inv3!F11": true, + "inv3!F12": true, + "inv3!F13": true, + "inv3!F14": true, + "inv3!F15": true, + "inv3!F16": true, + "inv3!F17": true, + "inv3!F18": true, + "sheet7!F6": true, + "sheet7!F7": true, + "sheet7!F8": true, + "sheet7!F9": true, + "sheet7!F10": true, + "sheet7!F11": true, + "sheet7!F12": true, + "sheet7!F13": true, + "sheet7!F14": true, + "sheet7!F15": true, + "sheet7!F16": true, + "sheet7!F17": true, + "sheet7!F18": true, + "sheet7!E6": true, + "sheet7!E7": true, + "sheet7!E8": true, + "sheet7!E9": true, + "sheet7!E10": true, + "sheet7!E11": true, + "sheet7!E12": true, + "sheet7!E13": true, + "sheet7!E14": true, + "sheet7!E15": true, + "sheet7!E16": true, + "sheet7!E17": true, + "sheet7!E18": true, + "sheet7!B6": true, + "sheet7!B2": true, + "sheet7!B7": true, + "sheet7!B8": true, + "sheet7!B9": true, + "sheet7!B10": true, + "sheet7!B11": true, + "sheet7!B12": true, + "sheet7!B13": true, + "sheet7!B14": true, + "sheet7!B15": true, + "sheet7!B16": true, + "sheet7!B17": true, + "sheet7!B18": true, + "sheet6!C5": true, + }, + constraints: { + "sheet6!B2": ["prompttext", "0", "1e10", "Invoice"], + "sheet6!F4": ["prompttext", "0", "1e10", "Date"], + "sheet6!G4": ["prompttext", "0", "1e10", "Date"], + "sheet6!B5": ["prompttext", "0", "1e10", "Invoice #"], + "sheet6!B7": ["prompttext", "0", "1e10", "From"], + "sheet6!B8": ["prompttext", "0", "1e10", "Company Name"], + "sheet6!B9": ["prompttext", "0", "1e10", "Street Address"], + "sheet6!B10": ["prompttext", "0", "1e10", "City, State, Zip"], + "sheet6!B11": ["prompttext", "0", "1e10", "Phone"], + "sheet6!B12": ["promptemail", "0", "1e10", "Email"], + "sheet6!B14": ["prompttext", "0", "1e10", "Bill To"], + "sheet6!B15": ["prompttext", "0", "1e10", "Name"], + "sheet6!B16": ["prompttext", "0", "1e10", "Company Name"], + "sheet6!B17": ["prompttext", "0", "1e10", "Street Address"], + "sheet6!B18": ["prompttext", "0", "1e10", "City, State, Zip"], + "sheet6!B19": ["prompttext", "0", "1e10", "Phone"], + "sheet6!B20": ["promptemail", "0", "1e10", "Email"], + "sheet6!B38": ["prompttext", "0", "1e10", "Notes"], + "sheet6!B39": ["prompttext", "0", "1e10", "Notes"], + "sheet6!B40": ["prompttext", "0", "1e10", "Notes"], + "sheet6!F36": ["prompttext", "0", "1e10", "Subtotal"], + "sheet6!G37": ["promptdecimal", "0", "1e10", "Tax Rate (0.00)"], + "sheet6!F37": ["prompttext", "0", "1e10", "Tax Rate"], + "sheet6!F39": ["prompttext", "0", "1e10", "Other"], + "sheet6!G39": ["promptdecimal", "0", "1e10", "Other"], + "sheet6!F38": ["prompttext", "0", "1e10", "Tax"], + "inv3!B6": ["prompttext", "0", "1e10", "Description"], + "inv3!B7": ["prompttext", "0", "1e10", "Description"], + "inv3!B8": ["prompttext", "0", "1e10", "Description"], + "inv3!B9": ["prompttext", "0", "1e10", "Description"], + "inv3!B10": ["prompttext", "0", "1e10", "Description"], + "inv3!B11": ["prompttext", "0", "1e10", "Description"], + "inv3!B12": ["prompttext", "0", "1e10", "Description"], + "inv3!B13": ["prompttext", "0", "1e10", "Description"], + "inv3!B14": ["prompttext", "0", "1e10", "Description"], + "inv3!B15": ["prompttext", "0", "1e10", "Description"], + "inv3!B16": ["prompttext", "0", "1e10", "Description"], + "inv3!B17": ["prompttext", "0", "1e10", "Description"], + "inv3!B18": ["prompttext", "0", "1e10", "Description"], + "sheet7!B6": ["prompttext", "0", "1e10", "Description"], + "sheet7!B7": ["prompttext", "0", "1e10", "Description"], + "sheet7!B8": ["prompttext", "0", "1e10", "Description"], + "sheet7!B9": ["prompttext", "0", "1e10", "Description"], + "sheet7!B10": ["prompttext", "0", "1e10", "Description"], + "sheet7!B11": ["prompttext", "0", "1e10", "Description"], + "sheet7!B12": ["prompttext", "0", "1e10", "Description"], + "sheet7!B13": ["prompttext", "0", "1e10", "Description"], + "sheet7!B14": ["prompttext", "0", "1e10", "Description"], + "sheet7!B15": ["prompttext", "0", "1e10", "Description"], + "sheet7!B16": ["prompttext", "0", "1e10", "Description"], + "sheet7!B17": ["prompttext", "0", "1e10", "Description"], + "sheet7!B18": ["prompttext", "0", "1e10", "Description"], + "sheet6!C5": ["promptnumeric", "0", "1e10", "Invoice#"], + "sheet7!E6": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E7": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E8": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E9": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E10": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E11": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E12": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E13": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E14": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E15": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E16": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E17": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E18": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!F6": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F7": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F8": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F9": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F10": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F11": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F12": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F13": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F14": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F15": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F16": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F17": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F18": ["promptdecimal", "0", "1e10", "Price"], + "inv3!E6": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E7": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E8": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E9": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E10": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E11": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E12": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E13": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E14": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E15": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E16": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E17": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E18": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!F6": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F7": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F8": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F9": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F10": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F11": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F12": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F13": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F14": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F15": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F16": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F17": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F18": ["promptdecimal", "0", "1e10", "Rate"], + }, + }, + }, + + footers: [ + { name: "Detail1", index: 1, isActive: true }, + { name: "Detail2", index: 2, isActive: false }, + { name: "Invoice", index: 3, isActive: false }, + ], + logoCell: null, + signatureCell: null, + cellMappings: {}, + }, + 2: { + template: "Mobile Invoice 2", + templateId: 2, + msc: { + numsheets: 2, + currentid: "sheet1", + currentname: "inv1", + sheetArr: { + sheet1: { + sheetstr: { + savestr: + "version:1.5\ncell:B1:b:::2::l:1:f:10\ncell:C1:b:::2::l:1:f:10\ncell:D1:b:::2::l:1:f:10\ncell:E1:b:::2::l:1:f:10\ncell:F1:b:::2::l:1:f:10\ncell:G1:b:::2::l:1:f:10\ncell:A2:b::2:::l:1:f:10\ncell:B2:t:INVOICE:b:1:1:1:1:f:13:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:10\ncell:D2:t::b:2::2::l:1:f:10\ncell:E2:t::b:2::2::l:1:f:10\ncell:F2:t::b:2::2::l:1:f:10\ncell:G2:t::b:2::2::l:1:f:10\ncell:A3:b::2:::l:3:f:10\ncell:B3:b:2:::2:l:3:f:10\ncell:C3:b:2::::l:1:f:10\ncell:D3:b:2::::l:1:f:10\ncell:E3:b:2::::l:1:f:10\ncell:F3:b:2::::l:1:f:10\ncell:G3:b:2:2:::l:3:f:10\ncell:A4:b::2:::l:3:f:10\ncell:B4:b::::2:l:3:f:6\ncell:C4:t:BILL TO\\c:f:9:colspan:2\ncell:E4:f:5:cf:2\ncell:G4:b::2:::l:3:f:10\ncell:A5:b::2:::l:3:f:10\ncell:B5:b::::2:l:3:f:7\ncell:C5:t:[Name]:f:9:cf:2:colspan:3\ncell:E5:f:1:cf:2\ncell:F5:t::l:2:f:10:tvf:4:rowspan:6\ncell:G5:t::b::1:::l:2:f:10\ncell:A6:b::2:::l:3:f:10\ncell:B6:b::::2:l:3:f:7\ncell:C6:t:[Street Address]:f:9:cf:2:colspan:3\ncell:E6:f:1:cf:2\ncell:F6:l:2:f:10\ncell:G6:b::1:::l:2:f:10\ncell:A7:b::2:::l:3:f:10\ncell:B7:b::::2:l:3:f:7\ncell:C7:t:[City, State, Zip]:f:9:cf:2:colspan:3\ncell:E7:f:1:cf:2\ncell:G7:b::2:::l:3:f:10\ncell:A8:b::2:::l:3:f:10\ncell:B8:b::::2:l:3:f:7\ncell:C8:t:Phone\\c:f:9:cf:2:colspan:3\ncell:D8:f:8:cf:2\ncell:E8:f:1:cf:2\ncell:G8:b::2:::l:3:f:10\ncell:A9:b::2:::l:3:f:10\ncell:B9:b::::2:l:3:f:7\ncell:C9:t:Email\\c:f:9:cf:2:colspan:3\ncell:E9:f:1:cf:2\ncell:G9:b::2:::l:3:f:10\ncell:A10:b::2:::l:3:f:10\ncell:B10:b::::2:l:3:f:7\ncell:C10:colspan:2\ncell:G10:b::2:::l:3:f:10\ncell:A11:b::2:::l:3:f:10\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:FROM\\c:f:9:colspan:2\ncell:E11:f:8:colspan:2\ncell:G11:b::2:::l:3:f:10\ncell:A12:b::2:::l:3:f:10\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[Name]:f:9:colspan:4\ncell:E12:colspan:2\ncell:G12:b::2:::l:3:f:10\ncell:A13:b::2:::l:3:f:10\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:[Street Address]:f:9:colspan:4\ncell:E13:colspan:2\ncell:G13:b::2:::l:3:f:10\ncell:A14:b::2:::l:3:f:10\ncell:B14:b::::2:l:3:f:7\ncell:C14:t:[City, State, Zip]:f:9:colspan:4\ncell:E14:colspan:2\ncell:G14:b::2:::l:3:f:10\ncell:A15:b::2:::l:3:f:10\ncell:B15:b::::2:l:3:f:7\ncell:C15:t:Phone\\c:f:9:colspan:4\ncell:D15:f:8:cf:2:colspan:3\ncell:E15:colspan:2\ncell:G15:b::2:::l:3:f:10\ncell:A16:b::2:::l:3:f:10\ncell:B16:b::::2:l:3:f:7\ncell:C16:t:Email\\c:f:9:colspan:4\ncell:E16:colspan:2\ncell:G16:b::2:::l:3:f:10\ncell:A17:b::2:::l:3:f:10\ncell:B17:b::::2:l:3:f:6\ncell:G17:b::2:::l:3:f:10\ncell:A18:b::2:::l:3:f:10\ncell:B18:b::::2:l:3:f:10\ncell:C18:t:INVOICE #\\c:f:9:cf:2:colspan:4\ncell:D18:v:1:l:4:f:9:cf:2:colspan:3\ncell:G18:b::2:::l:3:f:10\ncell:A19:b::2:::l:3:f:10\ncell:B19:b::::2:l:3:f:10\ncell:G19:b::2:::l:3:f:10\ncell:A20:b::2:::l:3:f:10\ncell:B20:b::::2:l:3:f:10\ncell:C20:t:DATE\\c:f:9:cf:2\ncell:D20:vtf:nd:45892:TODAY():f:9:cf:2:ntvf:3:colspan:2\ncell:G20:b::2:::l:3:f:10\ncell:A21:b::2:::l:3:f:10\ncell:B21:b::::2:l:3:f:6:cf:2\ncell:C21:b:::2::l:1:f:10\ncell:D21:b:::2::l:3:f:6:cf:2\ncell:E21:b:::2::l:1:f:10\ncell:F21:b:::2::l:1:f:10\ncell:G21:b::2:::l:3:f:10\ncell:A22:b::2:::l:3:f:10\ncell:B22:b::2::2:l:3:f:10\ncell:C22:t:Description:b:1::1:1:f:9:cf:1:colspan:3\ncell:D22:t:Description:b:1::1::l:3:f:6:cf:2:colspan:2:rowspan:1\ncell:E22:t::b:2:2:2::l:1:f:10\ncell:F22:t:Amount:b:1:1:1:1:f:9:cf:1\ncell:G22:b::2::2:l:3:f:10\ncell:A23:b::2:::l:3:f:10\ncell:B23:b::2::2:l:3:f:10\ncell:C23:b:1:1::1:f:2:cf:2:colspan:3\ncell:D23:b:1:1:::l:3:f:10:cf:2:colspan:2:rowspan:1\ncell:E23:t::b:2:2:2::l:1:f:10\ncell:F23:b:1:1:::f:2:ntvf:1\ncell:G23:b::2::2:l:3:f:10\ncell:A24:b::2:::l:3:f:10\ncell:B24:b::2::2:l:3:f:10:cf:2\ncell:C24:b::1::1:f:2:cf:2:colspan:3\ncell:D24:b::1:::l:1:f:10:colspan:2\ncell:E24:l:1:f:10\ncell:F24:b::1:::f:2:ntvf:1\ncell:G24:b::2::2:l:3:f:10\ncell:A25:b::2:::l:3:f:10\ncell:B25:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C25:b::1::1:f:2:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:2:ntvf:1\ncell:G25:b::2::2:l:3:f:10\ncell:A26:b::2:::l:3:f:10\ncell:B26:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C26:b::1::1:f:2:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:2:ntvf:1\ncell:G26:b::2::2:l:3:f:10\ncell:A27:b::2:::l:3:f:10\ncell:B27:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C27:b::1::1:f:2:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:2:ntvf:1\ncell:G27:b::2::2:l:3:f:10\ncell:A28:b::2:::l:3:f:10\ncell:B28:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C28:b::1::1:f:2:cf:2:colspan:3\ncell:D28:b::1:::colspan:2\ncell:F28:b::1:::f:2:ntvf:1\ncell:G28:b::2::2:l:3:f:10\ncell:A29:b::2:::l:3:f:10\ncell:B29:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C29:b::1::1:f:2:cf:2:colspan:3\ncell:D29:b::1:::colspan:2\ncell:F29:b::1:::f:2:ntvf:1\ncell:G29:b::2::2:l:3:f:10\ncell:A30:b::2:::l:3:f:10\ncell:B30:b::2::2:l:3:f:10:cf:2\ncell:C30:b::1::1:f:2:cf:2:colspan:3\ncell:D30:b::1:::colspan:2\ncell:F30:b::1:::f:2:ntvf:1\ncell:G30:b::2::2:l:3:f:10\ncell:A31:b::2:::l:3:f:10\ncell:B31:b::2::2:l:3:f:10:cf:2\ncell:C31:b::1::1:f:2:cf:2:colspan:3\ncell:D31:b::1:::colspan:2\ncell:F31:b::1:::f:2:ntvf:1\ncell:G31:b::2::2:l:3:f:10\ncell:A32:b::2:::l:3:f:10\ncell:B32:b::2::2:l:3:f:10:cf:2\ncell:C32:b::1::1:f:2:cf:2:colspan:3\ncell:D32:b::1:::colspan:2\ncell:F32:b::1:::f:2:ntvf:1\ncell:G32:b::2::2:l:3:f:10\ncell:A33:b::2:::l:3:f:10\ncell:B33:b::2::2:l:3:f:10:cf:2\ncell:C33:b::1::1:f:2:cf:2:colspan:3\ncell:D33:b::1:::colspan:2\ncell:F33:b::1:::f:2:ntvf:1\ncell:G33:b::2::2:l:3:f:10\ncell:A34:b::2:::l:3:f:10\ncell:B34:b::2::2:l:3:f:10:cf:2\ncell:C34:b::1::1:f:2:cf:2:colspan:3\ncell:D34:b::1:::colspan:2\ncell:F34:b::1:::f:2:ntvf:1\ncell:G34:b::2::2:l:3:f:10\ncell:A35:b::2:::l:3:f:7\ncell:B35:b::2::2:l:3:f:10:cf:2\ncell:C35:b::1:1:1:f:2:cf:2:colspan:3\ncell:D35:b::1:1::l:3:f:10:cf:2:colspan:2\ncell:E35:b:::2::l:3:f:10:cf:2\ncell:F35:b::1:1::f:2:ntvf:1\ncell:G35:b::2::2:l:3:f:10\ncell:A36:b::2:::l:3:f:6\ncell:B36:b::2::2:l:3:f:6\ncell:C36:t:TOTAL:b:1:1:1:1:f:11:cf:2:colspan:3\ncell:D36:b:2::2::l:3:f:12:cf:2\ncell:E36:b:2:2:2::l:3:f:12:cf:2\ncell:F36:vtf:n:0:SUM(F23\\cF35):b:1:1:1::f:11:ntvf:1\ncell:G36:b::2::2:l:3:f:7\ncell:A37:b::2:::l:3:f:10\ncell:B37:b::::2:l:3:f:6:cf:2\ncell:C37:b:2::::l:1:f:10\ncell:D37:b:2::::l:1:f:10\ncell:E37:b:2::::l:1:f:10\ncell:F37:b:2::::l:1:f:10\ncell:G37:b::2:::l:3:f:6\ncell:A38:b::2:::l:3:f:10\ncell:B38:b::::2:l:3:f:10\ncell:G38:b::2:::l:3:f:10\ncell:A39:b::2:::l:3:f:10\ncell:B39:b::::2:l:3:f:10:cf:2\ncell:C39:t:Thank you for your business:f:4:cf:1:colspan:4\ncell:D39:t:Thank you for your business:colspan:3\ncell:E39:t:Thank you for your business:f:3:colspan:2\ncell:F39:t::l:2:f:10\ncell:G39:b::2:::l:3:f:10\ncell:A40:b::2:::l:3:f:10\ncell:B40:b::::2:l:3:f:10\ncell:G40:b::2:::l:3:f:10\ncell:A41:b::2:::l:3:f:10\ncell:B41:b:::2:2:l:3:f:10\ncell:C41:b:::2::l:3:f:10\ncell:D41:b:::2::l:3:f:10\ncell:E41:b:::2::l:3:f:10\ncell:F41:b:::2::l:3:f:10\ncell:G41:b::2:2::l:3:f:10\ncell:B42:b:1:::\ncell:C42:b:1:::\ncell:D42:b:1:::\ncell:E42:b:1:::\ncell:F42:b:1:::\ncell:G42:b:1:::\ncol:A:w:10\ncol:B:w:10\ncol:C:w:62\ncol:D:w:73\ncol:E:w:15\ncol:F:w:68\ncol:G:w:10\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:15.75\nrow:36:h:15.75\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:41:h:14.25\nsheet:c:7:r:42:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:* 9pt Trebuchet MS\nfont:3:italic bold * Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Arial\nfont:7:normal bold 12pt Arial\nfont:8:normal normal * *\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 11pt Trebuchet MS\nfont:12:normal normal 12pt Arial\nfont:13:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nlayout:4:padding:* * * 4px;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + }, + name: "inv1", + hidden: "0", + }, + sheet2: { + sheetstr: { + savestr: + "version:1.5\ncell:B2:t:INVOICE:l:1:f:9:cf:1:colspan:6\ncell:C2:t::l:2:f:13\ncell:D2:t::l:2:f:13\ncell:E2:l:1:f:10:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:8:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:13\ncell:B3:f:6:cf:2:colspan:4\ncell:C3:t::l:2:f:13\ncell:D3:t::l:2:f:13\ncell:F3:l:1:f:7:cf:2\ncell:G3:l:1:f:14:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:14:cf:2\ncell:G4:vtf:nd:42081:TODAY():l:1:f:14:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:3:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:7:cf:2:colspan:2\ncell:G5:l:1:f:14:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:7:cf:2:colspan:2\ncell:G6:l:1:f:14:cf:2\ncell:B7:t:FROM\\c:f:12\ncell:F7:l:1:f:7:cf:2:tvf:4:colspan:2:rowspan:6\ncell:G7:l:1:f:14:cf:2\ncell:B8:t:[Company Name]:f:3:colspan:4\ncell:F8:l:1:f:7:cf:2:colspan:2\ncell:G8:l:1:f:14:cf:2\ncell:B9:t:[Street Address]:f:1:cf:2:colspan:4\ncell:F9:l:1:f:7\ncell:G9:l:1:f:14:cf:1\ncell:B10:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:G10:l:1:f:13\ncell:B11:t:Phone\\c :f:1:cf:2:colspan:4\ncell:B12:t:Email\\c:f:1:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:11:cf:2\ncell:B15:t:[Name]:f:1:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:1:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:1:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:1:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:1:cf:2:colspan:6\ncell:B20:t:Email\\c:f:1:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:14:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:13\ncell:D22:t::l:2:f:13\ncell:E22:t::l:2:f:13\ncell:F22:t::l:2:f:13\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:14:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:b:1:1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:4\ncell:D23:t::l:2:f:4\ncell:E23:t::l:2:f:4\ncell:F23:t::b::2:::l:1:f:4\ncell:G23:b::1::1:f:4:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:4\ncell:D24:t::l:2:f:4\ncell:E24:t::l:2:f:4\ncell:F24:t::b::2:::l:1:f:4\ncell:G24:b::1::1:f:4:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:4\ncell:D25:t::l:2:f:4\ncell:E25:t::l:2:f:4\ncell:F25:t::b::2:::l:1:f:4\ncell:G25:b::1::1:f:4:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:4\ncell:D26:t::l:2:f:4\ncell:E26:t::l:2:f:4\ncell:F26:t::b::2:::l:1:f:4\ncell:G26:b::1::1:f:4:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:4\ncell:D27:t::l:2:f:4\ncell:E27:t::l:2:f:4\ncell:F27:t::b::2:::l:1:f:4\ncell:G27:b::1::1:f:4:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:4\ncell:D28:t::l:2:f:4\ncell:E28:t::l:2:f:4\ncell:F28:t::b::2:::l:1:f:4\ncell:G28:b::1::1:f:4:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:4\ncell:D29:t::l:2:f:4\ncell:E29:t::l:2:f:4\ncell:F29:t::b::2:::l:1:f:4\ncell:G29:b::1::1:f:4:ntvf:1\ncell:A30:b::1::\ncell:B30:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:4\ncell:D30:t::l:2:f:4\ncell:E30:t::l:2:f:4\ncell:F30:t::b::2:::l:1:f:4\ncell:G30:b::1::1:f:4:ntvf:1\ncell:A31:b::1::\ncell:B31:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:4\ncell:D31:t::l:2:f:4\ncell:E31:t::l:2:f:4\ncell:F31:t::b::2:::l:1:f:4\ncell:G31:b::1::1:f:4:ntvf:1\ncell:A32:b::1::\ncell:B32:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:4\ncell:D32:t::l:2:f:4\ncell:E32:t::l:2:f:4\ncell:F32:t::b::2:::l:1:f:4\ncell:G32:b::1::1:f:4:ntvf:1\ncell:A33:b::1::\ncell:B33:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C33:t::l:2:f:4\ncell:D33:t::l:2:f:4\ncell:E33:t::l:2:f:4\ncell:F33:t::b::2:::l:1:f:4\ncell:G33:b::1::1:f:4:ntvf:1\ncell:A34:b::1::\ncell:B34:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C34:l:2:f:4\ncell:D34:l:2:f:4\ncell:E34:l:2:f:4\ncell:F34:b::2:::l:1:f:4\ncell:G34:b::1::1:f:4:ntvf:1\ncell:A35:b::1::\ncell:B35:b::1:1:1:f:4:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:4\ncell:D35:t::b:::2::l:1:f:4\ncell:E35:t::b:::2::l:1:f:4\ncell:F35:t::b::2:2::l:1:f:4\ncell:G35:b::1:1:1:f:4:ntvf:1\ncell:B36:b:2::::l:1:f:13\ncell:C36:b:2::::l:1:f:13\ncell:D36:b:2::::l:1:f:13\ncell:E36:b:2::::l:1:f:14\ncell:F36:t:Subtotal:b:2::::l:1:f:11\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:11:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:14:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:13\ncell:D37:t::b:::2::l:1:f:13\ncell:F37:t:Tax Rate:l:1:f:11\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:13\ncell:D38:t::b:2::::l:1:f:13\ncell:F38:t:Tax:l:1:f:11\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:13\ncell:D39:t::l:2:f:13\ncell:F39:t:Other:b:::1::l:1:f:11\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:13\ncell:D40:t::l:2:f:13\ncell:F40:t:TOTAL:b:1::::l:1:f:14\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:11:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:5:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:13\ncell:D43:t::l:2:f:13\ncell:E43:t::l:2:f:13\ncell:F43:t::l:2:f:13\ncell:G43:t::l:2:f:13\ncol:A:w:10\ncol:B:w:90\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:45\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 10pt Trebuchet MS\nfont:4:* 9pt Trebuchet MS\nfont:5:italic bold 10pt Trebuchet MS\nfont:6:italic normal * Trebuchet MS\nfont:7:normal bold 10pt Trebuchet MS\nfont:8:normal bold 14pt Trebuchet MS\nfont:9:normal bold 16pt Trebuchet MS\nfont:10:normal bold 28pt Trebuchet MS\nfont:11:normal normal * Trebuchet MS\nfont:12:normal normal 10pt *\nfont:13:normal normal 10pt Arial\nfont:14:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + }, + name: "inv2", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "inv1!B2": true, + "inv1!C5": true, + "inv1!C6": true, + "inv1!C7": true, + "inv1!C8": true, + "inv1!C9": true, + "inv1!C11": true, + "inv1!C12": true, + "inv1!C13": true, + "inv1!C14": true, + "inv1!C15": true, + "inv1!C16": true, + "inv1!C18": true, + "inv1!C20": true, + "inv1!D20": true, + "inv1!C23": true, + "inv1!C24": true, + "inv1!C25": true, + "inv1!C26": true, + "inv1!C27": true, + "inv1!C28": true, + "inv1!C29": true, + "inv1!C30": true, + "inv1!C31": true, + "inv1!C32": true, + "inv1!C33": true, + "inv1!C34": true, + "inv1!C35": true, + "inv1!F23": true, + "inv1!F24": true, + "inv1!F25": true, + "inv1!F26": true, + "inv1!F27": true, + "inv1!F28": true, + "inv1!F29": true, + "inv1!F30": true, + "inv1!F31": true, + "inv1!F32": true, + "inv1!F33": true, + "inv1!F34": true, + "inv1!F35": true, + "inv2!B2": true, + "inv2!F4": true, + "inv2!G4": true, + "inv2!B5": true, + "inv2!B7": true, + "inv2!B8": true, + "inv2!B9": true, + "inv2!B10": true, + "inv2!B11": true, + "inv2!B12": true, + "inv2!B14": true, + "inv2!B15": true, + "inv2!B16": true, + "inv2!B17": true, + "inv2!B18": true, + "inv2!B19": true, + "inv2!B20": true, + "inv2!B23": true, + "inv2!B24": true, + "inv2!B25": true, + "inv2!B26": true, + "inv2!B27": true, + "inv2!B28": true, + "inv2!B29": true, + "inv2!B30": true, + "inv2!B31": true, + "inv2!B32": true, + "inv2!B33": true, + "inv2!B34": true, + "inv2!B35": true, + "inv2!G23": true, + "inv2!G24": true, + "inv2!G25": true, + "inv2!G26": true, + "inv2!G27": true, + "inv2!G28": true, + "inv2!G29": true, + "inv2!G30": true, + "inv2!G31": true, + "inv2!G32": true, + "inv2!G33": true, + "inv2!G34": true, + "inv2!G35": true, + "inv2!B38": true, + "inv2!B39": true, + "inv2!B40": true, + "inv2!F36": true, + "inv2!G37": true, + "inv2!F37": true, + "inv2!F39": true, + "inv2!G39": true, + "inv2!F38": true, + "inv1!D8": true, + "inv1!D15": true, + "inv1!D18": true, + "inv2!C5": true, + }, + constraints: { + "inv1!C5": ["prompttext", "0", "1e10", "Name"], + "inv1!C6": ["prompttext", "0", "1e10", "Street Address"], + "inv1!C7": ["prompttext", "0", "1e10", "City, State, Zip"], + "inv1!C8": ["prompttext", "0", "1e10", "Phone"], + "inv1!C9": ["promptemail", "0", "1e10", "Email"], + "inv1!C11": ["prompttext", "0", "1e10", "From"], + "inv1!C12": ["prompttext", "0", "1e10", "Name"], + "inv1!C13": ["prompttext", "0", "1e10", "Street Address"], + "inv1!C14": ["prompttext", "0", "1e10", "City, State, Zip"], + "inv1!C15": ["prompttext", "0", "1e10", "Phone"], + "inv1!C16": ["promptemail", "0", "1e10", "Email"], + "inv1!C18": ["prompttext", "0", "1e10", "Invoice #"], + "inv1!C20": ["prompttext", "0", "1e10", "Date"], + "inv1!D20": ["prompttext", "0", "1e10", "Date"], + "inv1!C23": ["prompttext", "0", "1e10", "Description"], + "inv1!C24": ["prompttext", "0", "1e10", "Description"], + "inv1!C25": ["prompttext", "0", "1e10", "Description"], + "inv1!C26": ["prompttext", "0", "1e10", "Description"], + "inv1!C27": ["prompttext", "0", "1e10", "Description"], + "inv1!C28": ["prompttext", "0", "1e10", "Description"], + "inv1!C29": ["prompttext", "0", "1e10", "Description"], + "inv1!C30": ["prompttext", "0", "1e10", "Description"], + "inv1!C31": ["prompttext", "0", "1e10", "Description"], + "inv1!C32": ["prompttext", "0", "1e10", "Description"], + "inv1!C33": ["prompttext", "0", "1e10", "Description"], + "inv1!C34": ["prompttext", "0", "1e10", "Description"], + "inv1!C35": ["prompttext", "0", "1e10", "Description"], + "inv1!F23": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F24": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F25": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F26": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F27": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F28": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F29": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F30": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F31": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F32": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F33": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F34": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F35": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!B2": ["prompttext", "0", "1e10", "Invoice"], + "inv2!F4": ["prompttext", "0", "1e10", "Date"], + "inv2!G4": ["prompttext", "0", "1e10", "Date"], + "inv2!B5": ["prompttext", "0", "1e10", "Invoice #"], + "inv2!B7": ["prompttext", "0", "1e10", "From"], + "inv2!B8": ["prompttext", "0", "1e10", "Company Name"], + "inv2!B9": ["prompttext", "0", "1e10", "Street Address"], + "inv2!B10": ["prompttext", "0", "1e10", "City, State, Zip"], + "inv2!B11": ["prompttext", "0", "1e10", "Phone"], + "inv2!B12": ["promptemail", "0", "1e10", "Email"], + "inv2!B14": ["prompttext", "0", "1e10", "Bill To"], + "inv2!B15": ["prompttext", "0", "1e10", "Name"], + "inv2!B16": ["prompttext", "0", "1e10", "Company Name"], + "inv2!B17": ["prompttext", "0", "1e10", "Street Address"], + "inv2!B18": ["prompttext", "0", "1e10", "City, State, Zip"], + "inv2!B19": ["prompttext", "0", "1e10", "Phone"], + "inv2!B20": ["promptemail", "0", "1e10", "Email"], + "inv2!B23": ["prompttext", "0", "1e10", "Description"], + "inv2!B24": ["prompttext", "0", "1e10", "Description"], + "inv2!B25": ["prompttext", "0", "1e10", "Description"], + "inv2!B26": ["prompttext", "0", "1e10", "Description"], + "inv2!B27": ["prompttext", "0", "1e10", "Description"], + "inv2!B28": ["prompttext", "0", "1e10", "Description"], + "inv2!B29": ["prompttext", "0", "1e10", "Description"], + "inv2!B30": ["prompttext", "0", "1e10", "Description"], + "inv2!B31": ["prompttext", "0", "1e10", "Description"], + "inv2!B32": ["prompttext", "0", "1e10", "Description"], + "inv2!B33": ["prompttext", "0", "1e10", "Description"], + "inv2!B34": ["prompttext", "0", "1e10", "Description"], + "inv2!B35": ["prompttext", "0", "1e10", "Description"], + "inv2!G23": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G24": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G25": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G26": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G27": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G28": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G29": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G30": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G31": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G32": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G33": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G34": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!G35": ["promptdecimal", "0", "1e10", "Amount"], + "inv2!B38": ["prompttext", "0", "1e10", "Notes"], + "inv2!B39": ["prompttext", "0", "1e10", "Notes"], + "inv2!B40": ["prompttext", "0", "1e10", "Notes"], + "inv2!F36": ["prompttext", "0", "1e10", "Subtotal"], + "inv2!G37": ["promptdecimal", "0", "1e10", "Tax Rate (0.00)"], + "inv2!F37": ["prompttext", "0", "1e10", "Tax Rate"], + "inv2!F39": ["prompttext", "0", "1e10", "Other"], + "inv2!G39": ["promptdecimal", "0", "1e10", "Other"], + "inv2!F38": ["prompttext", "0", "1e10", "Tax"], + "inv1!D8": ["prompttext", "0", "1e10", "Phone"], + "inv1!D15": ["prompttext", "0", "1e10", "Phone"], + "inv1!D18": ["promptnumeric", "0", "1e10", "Invoice#"], + "inv2!C5": ["promptnumeric", "0", "1e10", "Invoice#"], + }, + }, + }, + + footers: [ + { name: "Invoice 1", index: 1, isActive: true }, + { name: "Invoice 2", index: 2, isActive: false }, + ], + logoCell: null, + signatureCell: null, + cellMappings: {}, + }, +}; diff --git a/src/utils/templateInitializer.ts b/src/utils/templateInitializer.ts new file mode 100644 index 0000000..67880d4 --- /dev/null +++ b/src/utils/templateInitializer.ts @@ -0,0 +1,211 @@ +import { DATA, TemplateData } from "../templates"; +import { TemplateMetadata } from "../components/Storage/LocalStorage"; +import { TemplateManager } from "./templateManager"; + +/** + * Template Initialization System + * Handles the setup and migration to the new multi-template architecture + */ +export class TemplateInitializer { + /** + * Initialize the application with multi-template support + */ + static async initializeApp(): Promise { + console.log("🚀 Initializing Multi-Template Architecture..."); + + try { + // Validate template data + this.validateTemplateData(); + + // Setup default template metadata + this.setupDefaultMetadata(); + + // Initialize template registry + await this.initializeTemplateRegistry(); + + console.log("✅ Multi-Template Architecture initialized successfully"); + } catch (error) { + console.error( + "❌ Failed to initialize multi-template architecture:", + error + ); + throw error; + } + } + + /** + * Validate template data structure + */ + private static validateTemplateData(): void { + console.log("🔍 Validating template data..."); + + for (const [id, template] of Object.entries(DATA)) { + if (!template.template || !template.templateId) { + throw new Error(`Invalid template data for template ${id}`); + } + + if (!template.msc || !template.msc.sheetArr) { + throw new Error(`Missing MSC data for template ${id}`); + } + + console.log(`✓ Template ${id}: ${template.template} - Valid`); + } + } + + /** + * Setup default metadata for existing templates + */ + private static setupDefaultMetadata(): void { + console.log("⚙️ Setting up default metadata..."); + + for (const [id, template] of Object.entries(DATA)) { + // Ensure footers exist + if (!template.footers || template.footers.length === 0) { + template.footers = [ + { name: template.template, index: 1, isActive: true }, + ]; + } + + // Ensure cellMappings exist + if (!template.cellMappings) { + template.cellMappings = TemplateManager.generateDefaultCellMappings( + template.templateId + ); + } + + // Ensure logoCell and signatureCell exist + if (template.logoCell === undefined) { + template.logoCell = null; + } + if (template.signatureCell === undefined) { + template.signatureCell = null; + } + + console.log(`✓ Metadata setup complete for template ${id}`); + } + } + + /** + * Initialize template registry in localStorage + */ + private static async initializeTemplateRegistry(): Promise { + console.log("📝 Initializing template registry..."); + + const registry = { + version: "2.0.0", + templates: Object.keys(DATA).map((id) => { + const template = DATA[parseInt(id)]; + return { + id: template.templateId, + name: template.template, + version: "1.0.0", + created: new Date().toISOString(), + modified: new Date().toISOString(), + }; + }), + initialized: new Date().toISOString(), + }; + + try { + if (typeof window !== "undefined" && window.localStorage) { + localStorage.setItem("template_registry", JSON.stringify(registry)); + console.log("✓ Template registry saved to localStorage"); + } + } catch (error) { + console.warn( + "⚠️ Could not save template registry to localStorage:", + error + ); + } + } + + /** + * Get template by ID + */ + static getTemplate(templateId: number): TemplateData | null { + return DATA[templateId] || null; + } + + /** + * Get all available templates + */ + static getAllTemplates(): TemplateData[] { + return Object.values(DATA); + } + + /** + * Get template metadata + */ + static getTemplateMetadata(templateId: number): TemplateMetadata | null { + const template = this.getTemplate(templateId); + if (!template) return null; + + return TemplateManager.extractMetadata(template); + } + + /** + * Create MSC content for a template + */ + static createMSCContent(templateId: number): string | null { + const template = this.getTemplate(templateId); + if (!template) return null; + + try { + // Convert the MSC object to a string format + return JSON.stringify(template.msc); + } catch (error) { + console.error( + `Error creating MSC content for template ${templateId}:`, + error + ); + return null; + } + } + + /** + * Check if the app has been initialized with the new architecture + */ + static async isInitialized(): Promise { + try { + if (typeof window !== "undefined" && window.localStorage) { + const registry = localStorage.getItem("template_registry"); + if (registry) { + const parsed = JSON.parse(registry); + return parsed.version === "2.0.0"; + } + } + return false; + } catch (error) { + return false; + } + } + + /** + * Migration utility for existing files + */ + static async migrateExistingFiles(): Promise { + console.log("🔄 Starting migration of existing files..."); + + // This would be implemented to migrate existing files to the new structure + // For now, we'll just log that it should be implemented + console.log( + "⚠️ File migration not yet implemented - manual migration required" + ); + } + + /** + * Reset the template system (for development/testing) + */ + static async reset(): Promise { + console.log("🔄 Resetting template system..."); + + try { + if (typeof window !== "undefined" && window.localStorage) { + localStorage.removeItem("template_registry"); + console.log("✓ Template registry cleared"); + } + } catch (error) { + console.warn("⚠️ Could not clear template registry:", error); + } + } +} diff --git a/src/utils/templateManager.ts b/src/utils/templateManager.ts new file mode 100644 index 0000000..d6bdf8c --- /dev/null +++ b/src/utils/templateManager.ts @@ -0,0 +1,157 @@ +import { TemplateData } from "../templates"; +import { TemplateMetadata } from "../components/Storage/LocalStorage"; + +/** + * Template Manager Utility + * Handles multi-template operations and metadata management + */ +export class TemplateManager { + /** + * Extract template metadata from template data + */ + static extractMetadata(templateData: TemplateData): TemplateMetadata { + return { + template: templateData.template, + templateId: templateData.templateId, + footers: templateData.footers, + logoCell: templateData.logoCell, + signatureCell: templateData.signatureCell, + cellMappings: templateData.cellMappings, + }; + } + + /** + * Create a complete template metadata object with MSC content + */ + static createTemplateWithMSC( + templateData: TemplateData, + mscContent: string + ): { + metadata: TemplateMetadata; + mscContent: string; + } { + return { + metadata: this.extractMetadata(templateData), + mscContent, + }; + } + + /** + * Get template-specific storage key + */ + static getTemplateStorageKey(templateId: number, baseName: string): string { + return `template_${templateId}_${baseName}`; + } + + /** + * Parse template ID from storage key + */ + static parseTemplateIdFromKey(storageKey: string): number | null { + const match = storageKey.match(/^template_(\d+)_/); + return match ? parseInt(match[1], 10) : null; + } + + /** + * Validate template metadata + */ + static validateMetadata(metadata: TemplateMetadata): boolean { + return ( + typeof metadata.template === "string" && + typeof metadata.templateId === "number" && + Array.isArray(metadata.footers) && + metadata.footers.every( + (footer) => + typeof footer.name === "string" && + typeof footer.index === "number" && + typeof footer.isActive === "boolean" + ) + ); + } + + /** + * Merge cell mappings from different sources + */ + static mergeCellMappings( + existing: TemplateMetadata["cellMappings"], + newMappings: TemplateMetadata["cellMappings"] + ): TemplateMetadata["cellMappings"] { + const merged = { ...existing }; + + for (const [headingName, cellMappings] of Object.entries(newMappings)) { + if (merged[headingName]) { + merged[headingName] = { ...merged[headingName], ...cellMappings }; + } else { + merged[headingName] = { ...cellMappings }; + } + } + + return merged; + } + + /** + * Filter files by template ID + */ + static filterFilesByTemplate( + files: Record, + templateId: number + ): Record { + const filtered: Record = {}; + + for (const [fileName, fileData] of Object.entries(files)) { + if (fileData.templateMetadata?.templateId === templateId) { + filtered[fileName] = fileData; + } + } + + return filtered; + } + + /** + * Get unique template IDs from files + */ + static getUniqueTemplateIds(files: Record): number[] { + const templateIds = new Set(); + + for (const fileData of Object.values(files)) { + if (fileData.templateMetadata?.templateId) { + templateIds.add(fileData.templateMetadata.templateId); + } + } + + return Array.from(templateIds).sort(); + } + + /** + * Generate default cell mappings for a template + */ + static generateDefaultCellMappings( + templateId: number + ): TemplateMetadata["cellMappings"] { + // Default mappings based on common invoice patterns + const defaultMappings: TemplateMetadata["cellMappings"] = { + "Company Information": { + B8: { heading: "Company Name", datatype: "text" }, + B9: { heading: "Street Address", datatype: "text" }, + B10: { heading: "City, State, Zip", datatype: "text" }, + B11: { heading: "Phone", datatype: "text" }, + B12: { heading: "Email", datatype: "email" }, + }, + "Invoice Details": { + B2: { heading: "Invoice Title", datatype: "text" }, + B5: { heading: "Invoice Number", datatype: "text" }, + F4: { heading: "Date", datatype: "date" }, + G4: { heading: "Due Date", datatype: "date" }, + }, + "Bill To": { + B15: { heading: "Customer Name", datatype: "text" }, + B16: { heading: "Customer Company", datatype: "text" }, + B17: { heading: "Customer Address", datatype: "text" }, + B18: { heading: "Customer City, State, Zip", datatype: "text" }, + B19: { heading: "Customer Phone", datatype: "text" }, + B20: { heading: "Customer Email", datatype: "email" }, + }, + }; + + return defaultMappings; + } +} From 2af0a44e0debf4031294b54544196690a95059b3 Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Wed, 27 Aug 2025 22:52:59 +0530 Subject: [PATCH 4/9] dynamic form generation --- src/App.tsx | 4 + src/components/DynamicFormDemo.tsx | 63 +++ src/components/DynamicInvoiceForm.tsx | 387 ++++++++++++++++++ src/components/InvoiceForm.css | 27 ++ src/components/Storage/LocalStorage.ts | 18 +- src/pages/Home.tsx | 3 +- src/template2.ts | 522 +++++++++++++++++++++++++ src/templates-meta.ts | 12 + src/templates.ts | 175 ++++++++- src/utils/dynamicFormManager.ts | 295 ++++++++++++++ src/utils/templateManager.ts | 49 ++- 11 files changed, 1514 insertions(+), 41 deletions(-) create mode 100644 src/components/DynamicFormDemo.tsx create mode 100644 src/components/DynamicInvoiceForm.tsx create mode 100644 src/template2.ts create mode 100644 src/utils/dynamicFormManager.ts diff --git a/src/App.tsx b/src/App.tsx index 536e5df..4f8c571 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,6 +9,7 @@ import Home from "./pages/Home"; import FilesPage from "./pages/FilesPage"; import SettingsPage from "./pages/SettingsPage"; import LandingPage from "./pages/LandingPage"; +import DynamicFormDemo from "./components/DynamicFormDemo"; import { ThemeProvider, useTheme } from "./contexts/ThemeContext"; import { InvoiceProvider } from "./contexts/InvoiceContext"; import PWAUpdatePrompt from "./components/PWAUpdatePrompt"; @@ -88,6 +89,9 @@ const AppContent: React.FC = () => { + + + diff --git a/src/components/DynamicFormDemo.tsx b/src/components/DynamicFormDemo.tsx new file mode 100644 index 0000000..40ee357 --- /dev/null +++ b/src/components/DynamicFormDemo.tsx @@ -0,0 +1,63 @@ +import React, { useState } from "react"; +import { + IonContent, + IonHeader, + IonPage, + IonTitle, + IonToolbar, + IonButton, + IonCard, + IonCardContent, + IonCardHeader, + IonCardTitle, +} from "@ionic/react"; +import DynamicInvoiceForm from "../components/DynamicInvoiceForm"; + +const DynamicFormDemo: React.FC = () => { + const [showForm, setShowForm] = useState(false); + + return ( + + + + Dynamic Form Demo + + + +
+ + + Dynamic Invoice Form System + + +

+ This demo showcases the dynamic form generation system that creates forms based on: +

+
    +
  • Template cell mappings
  • +
  • Active footer indices
  • +
  • Field type detection
  • +
  • Dynamic validation
  • +
+ + setShowForm(true)} + style={{ marginTop: "20px" }} + > + Open Dynamic Form + +
+
+
+ + setShowForm(false)} + /> +
+
+ ); +}; + +export default DynamicFormDemo; diff --git a/src/components/DynamicInvoiceForm.tsx b/src/components/DynamicInvoiceForm.tsx new file mode 100644 index 0000000..a6031db --- /dev/null +++ b/src/components/DynamicInvoiceForm.tsx @@ -0,0 +1,387 @@ +import React, { useState, useEffect, useMemo } from "react"; +import { + IonModal, + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonItem, + IonLabel, + IonInput, + IonButton, + IonButtons, + IonIcon, + IonGrid, + IonRow, + IonCol, + IonCard, + IonCardHeader, + IonCardTitle, + IonCardContent, + IonList, + IonToast, + IonItemDivider, + IonTextarea, + IonFab, + IonFabButton, + IonSelect, + IonSelectOption, + IonChip, +} from "@ionic/react"; +import { close, save, add, trash, layers } from "ionicons/icons"; +import { DATA, TemplateData } from "../templates"; +import { useInvoice } from "../contexts/InvoiceContext"; +import { + addInvoiceData, + clearInvoiceData, +} from "./socialcalc/modules/invoice.js"; +import { + DynamicFormManager, + DynamicFormSection, + DynamicFormField, + ProcessedFormData +} from "../utils/dynamicFormManager"; +import "./InvoiceForm.css"; + +interface DynamicInvoiceFormProps { + isOpen: boolean; + onClose: () => void; +} + +const DynamicInvoiceForm: React.FC = ({ isOpen, onClose }) => { + const { activeTempId } = useInvoice(); + const [activeFooterIndex, setActiveFooterIndex] = useState(1); + const [formData, setFormData] = useState({}); + const [showToast, setShowToast] = useState(false); + const [toastMessage, setToastMessage] = useState(""); + const [toastColor, setToastColor] = useState<"success" | "danger" | "warning">("success"); + + // Get current template data + const currentTemplate = useMemo(() => { + return DATA[activeTempId]; + }, [activeTempId]); + + // Get active footer based on activeFooterIndex + const activeFooter = useMemo(() => { + return currentTemplate?.footers.find(footer => footer.index === activeFooterIndex); + }, [currentTemplate, activeFooterIndex]); + + // Generate form sections based on cellMappings and active footer + const formSections = useMemo(() => { + if (!currentTemplate) return []; + return DynamicFormManager.getFormSectionsForFooter(currentTemplate, activeFooterIndex); + }, [currentTemplate, activeFooterIndex]); + + // Initialize form data when form sections change + useEffect(() => { + const initData = DynamicFormManager.initializeFormData(formSections); + setFormData(initData); + }, [formSections]); + + const showToastMessage = ( + message: string, + color: "success" | "danger" | "warning" = "success" + ) => { + setToastMessage(message); + setToastColor(color); + setShowToast(true); + }; + + const handleFieldChange = (sectionTitle: string, fieldLabel: string, value: string) => { + setFormData(prev => ({ + ...prev, + [sectionTitle]: { + ...prev[sectionTitle], + [fieldLabel]: value, + } + })); + }; + + const handleItemChange = (sectionTitle: string, itemIndex: number, fieldName: string, value: string) => { + setFormData(prev => ({ + ...prev, + [sectionTitle]: prev[sectionTitle].map((item: any, index: number) => + index === itemIndex ? { ...item, [fieldName]: value } : item + ) + })); + }; + + const handleSave = async () => { + try { + // Validate form data + const validation = DynamicFormManager.validateFormData(formData, formSections); + if (!validation.isValid) { + showToastMessage(`Validation errors: ${validation.errors.join(', ')}`, "warning"); + return; + } + + // Convert form data to spreadsheet format + const cellData = DynamicFormManager.convertToSpreadsheetFormat(formData, formSections, activeFooterIndex); + + // Create invoice data object + const invoiceData = { + templateId: activeTempId, + footerIndex: activeFooterIndex, + cellData, + dynamicData: formData, + }; + + await addInvoiceData(invoiceData); + showToastMessage("Invoice data saved successfully!", "success"); + + // Close modal after a short delay + setTimeout(() => { + onClose(); + }, 1500); + } catch (error) { + console.error("Error saving invoice data:", error); + showToastMessage("Failed to save invoice data", "danger"); + } + }; + + const handleClear = async () => { + try { + await clearInvoiceData(); + // Reset form data + const initData = DynamicFormManager.initializeFormData(formSections); + setFormData(initData); + showToastMessage("Form data cleared successfully!", "success"); + } catch (error) { + console.error("Error clearing form data:", error); + showToastMessage("Failed to clear form data", "danger"); + } + }; + + const renderField = (field: DynamicFormField, sectionTitle: string) => { + const value = formData[sectionTitle]?.[field.label] || ""; + + switch (field.type) { + case 'textarea': + return ( + + {field.label} + handleFieldChange(sectionTitle, field.label, e.detail.value!)} + placeholder={`Enter ${field.label.toLowerCase()}`} + rows={3} + /> + + ); + case 'email': + return ( + + {field.label} + handleFieldChange(sectionTitle, field.label, e.detail.value!)} + placeholder={`Enter ${field.label.toLowerCase()}`} + /> + + ); + case 'number': + return ( + + {field.label} + handleFieldChange(sectionTitle, field.label, e.detail.value!)} + placeholder={`Enter ${field.label.toLowerCase()}`} + /> + + ); + case 'decimal': + return ( + + {field.label} + handleFieldChange(sectionTitle, field.label, e.detail.value!)} + placeholder={`Enter ${field.label.toLowerCase()}`} + /> + + ); + default: + return ( + + {field.label} + handleFieldChange(sectionTitle, field.label, e.detail.value!)} + placeholder={`Enter ${field.label.toLowerCase()}`} + /> + + ); + } + }; + + const renderItemsSection = (section: DynamicFormSection) => { + if (!section.itemsConfig || !formData[section.title]) return null; + + const items = formData[section.title] as any[]; + + return ( + + + {section.title} + + + {items.map((item, index) => ( +
+ + Item {index + 1} + + {Object.entries(section.itemsConfig!.content).map(([fieldName, cellColumn]) => ( + + {fieldName} + handleItemChange(section.title, index, fieldName, e.detail.value!)} + placeholder={`Enter ${fieldName.toLowerCase()}`} + /> + + ))} +
+ ))} +
+
+ ); + }; + + const renderSection = (section: DynamicFormSection) => { + if (section.isItems) { + return renderItemsSection(section); + } + + return ( + + + {section.title} + + + + {section.fields.map(field => renderField(field, section.title))} + + + + ); + }; + + if (!currentTemplate) { + return ( + + + + Dynamic Invoice Form + + + + + + + + +
+

No template found for the current selection.

+
+
+
+ ); + } + + return ( + <> + + + + Dynamic Invoice Form + + + + {currentTemplate.template} + + + + + + + + + + + + {/* Footer Selection */} + {currentTemplate.footers.length > 1 && ( + + + Select Footer + + + + Active Footer + setActiveFooterIndex(e.detail.value)} + > + {currentTemplate.footers.map(footer => ( + + {footer.name} + + ))} + + + + + )} + + {/* Dynamic Form Sections */} + + {formSections.map(section => renderSection(section))} + + + {/* Action Buttons */} +
+ + + Save Invoice Data + + + + Clear All Data + +
+
+
+ + setShowToast(false)} + message={toastMessage} + duration={3000} + color={toastColor} + /> + + ); +}; + +export default DynamicInvoiceForm; diff --git a/src/components/InvoiceForm.css b/src/components/InvoiceForm.css index e552f0f..af1b418 100644 --- a/src/components/InvoiceForm.css +++ b/src/components/InvoiceForm.css @@ -25,6 +25,33 @@ margin-bottom: 16px; } +/* Dynamic form specific styles */ +.item-group { + margin-bottom: 20px; + padding: 10px; + border: 1px solid var(--ion-color-light-shade); + border-radius: 8px; + background: var(--ion-color-light-tint); +} + +.item-group:last-child { + margin-bottom: 0; +} + +.dynamic-form-section { + margin-bottom: 20px; +} + +.footer-selection { + margin-bottom: 20px; + background: var(--ion-color-primary-tint); +} + +.footer-selection ion-card-header { + background: var(--ion-color-primary); + color: var(--ion-color-primary-contrast); +} + @media (max-width: 768px) { .invoice-form-modal { --width: 100vw; diff --git a/src/components/Storage/LocalStorage.ts b/src/components/Storage/LocalStorage.ts index aa8469a..a9a0659 100644 --- a/src/components/Storage/LocalStorage.ts +++ b/src/components/Storage/LocalStorage.ts @@ -10,14 +10,18 @@ export interface TemplateMetadata { index: number; isActive: boolean; }[]; - logoCell: string | null; - signatureCell: string | null; + logoCell: string | { [footerIndex: number]: string }; + signatureCell: string | { [footerIndex: number]: string }; cellMappings: { - [headingName: string]: { - [cellName: string]: { - heading: string; - datatype: string; - }; + [footerIndex: number]: { + [fieldName: string]: + | string + | { [subField: string]: any } + | { + name?: string; + Range?: { start: number; end: number }; + Content?: { [fieldName: string]: string }; + }; }; }; } diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 0eb426d..7b78a19 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -56,6 +56,7 @@ import { useTheme } from "../contexts/ThemeContext"; import { useInvoice } from "../contexts/InvoiceContext"; import { useHistory, useParams } from "react-router-dom"; import InvoiceForm from "../components/InvoiceForm"; +import DynamicInvoiceForm from "../components/DynamicInvoiceForm"; // import WalletConnection from "../components/wallet/WalletConnection"; import { isDefaultFileEmpty, @@ -810,7 +811,7 @@ const Home: React.FC = () => {
- setShowInvoiceForm(false)} /> diff --git a/src/template2.ts b/src/template2.ts new file mode 100644 index 0000000..ae115c1 --- /dev/null +++ b/src/template2.ts @@ -0,0 +1,522 @@ +const a = { + 3: { + template: "Web Invoice 1", + templateId: 3, + msc: { + numsheets: 2, + currentid: "sheet1", + currentname: "typei", + sheetArr: { + sheet1: { + sheetstr: { + savestr: + "version:1.5\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE:b:1:1:1:1:f:6:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:t::b:2::2::l:1:f:7\ncell:G2:t::b:2::2::l:1:f:7\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2:::2:l:3:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:2::::l:1:f:7\ncell:G3:b:2:2:::l:3:f:7\ncell:A4:b::2:::l:3:f:7\ncell:B4:b::::2:l:3:f:3\ncell:C4:t:INVOICE # \\c:f:2:cf:2\ncell:D4:v:1:f:2:cf:2\ncell:F4:tvf:4:rowspan:4\ncell:G4:b::2:::l:3:f:7\ncell:A5:b::2:::l:3:f:7\ncell:B5:b::::2:l:3:f:4\ncell:F5:t::l:2:f:7\ncell:G5:t::b::1:::l:2:f:7\ncell:A6:b::2:::l:3:f:7\ncell:B6:b::::2:l:3:f:4\ncell:C6:t:INVOICE DATE\\c:f:2:cf:2\ncell:D6:f:2:cf:2:ntvf:3\ncell:F6:l:2:f:7\ncell:G6:b::1:::l:2:f:7\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::::2:l:3:f:4\ncell:G7:b::2:::l:3:f:7\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::::2:l:3:f:4\ncell:G8:b::2:::l:3:f:7\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::::2:l:3:f:4\ncell:C9:t:BILL TO\\c:l:3:f:2\ncell:E9:t:FROM\\c:f:2:cf:2\ncell:G9:b::2:::l:3:f:7\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::::2:l:3:f:3\ncell:C10:t:[Name]:f:1:cf:2:colspan:2\ncell:E10:t:[Name]:f:1:cf:2:colspan:2\ncell:G10:b::2:::l:3:f:7\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:E11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:G11:b::2:::l:3:f:7\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:E12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:G12:b::2:::l:3:f:7\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:Phone\\c:f:1:cf:2:colspan:2\ncell:E13:t:Phone\\c:f:1:cf:2:colspan:2\ncell:G13:b::2:::l:3:f:7\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::::2:l:3:f:3:cf:2\ncell:C14:b:::2::l:1:f:7\ncell:D14:b:::2::l:3:f:3:cf:2\ncell:E14:b:::2::l:1:f:7\ncell:F14:b:::2::l:1:f:7\ncell:G14:b::2:::l:3:f:7\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::2::2:l:3:f:7\ncell:C15:t:Description:b:1::1:1:f:2:cf:1:colspan:3\ncell:D15:t:Description:b:1::1::l:3:f:3:cf:2:colspan:2:rowspan:1\ncell:E15:t::b:2:2:2::l:1:f:7\ncell:F15:t:Amount:b:1:1:1:1:f:2:cf:1\ncell:G15:b::2::2:l:3:f:7\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::2::2:l:3:f:7\ncell:C16:b:1:1::1:f:1:cf:2:colspan:3\ncell:D16:b:1:1:::l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:E16:t::b:2:2:2::l:1:f:7\ncell:F16:b:1:1:::f:1:ntvf:1\ncell:G16:b::2::2:l:3:f:7\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::2::2:l:3:f:7:cf:2\ncell:C17:b::1::1:f:1:cf:2:colspan:3\ncell:D17:b::1:::l:1:f:7:colspan:2\ncell:E17:l:1:f:7\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::2::2:l:3:f:7\ncell:A18:b::2:::l:3:f:7\ncell:B18:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C18:b::1::1:f:1:cf:2:colspan:3\ncell:D18:b::1:::colspan:2\ncell:F18:b::1:::f:1:ntvf:1\ncell:G18:b::2::2:l:3:f:7\ncell:A19:b::2:::l:3:f:7\ncell:B19:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C19:b::1::1:f:1:cf:2:colspan:3\ncell:D19:b::1:::colspan:2\ncell:F19:b::1:::f:1:ntvf:1\ncell:G19:b::2::2:l:3:f:7\ncell:A20:b::2:::l:3:f:7\ncell:B20:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C20:b::1::1:f:1:cf:2:colspan:3\ncell:D20:b::1:::colspan:2\ncell:F20:b::1:::f:1:ntvf:1\ncell:G20:b::2::2:l:3:f:7\ncell:A21:b::2:::l:3:f:7\ncell:B21:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C21:b::1::1:f:1:cf:2:colspan:3\ncell:D21:b::1:::colspan:2\ncell:F21:b::1:::f:1:ntvf:1\ncell:G21:b::2::2:l:3:f:7\ncell:A22:b::2:::l:3:f:7\ncell:B22:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C22:b::1::1:f:1:cf:2:colspan:3\ncell:D22:b::1:::colspan:2\ncell:F22:b::1:::f:1:ntvf:1\ncell:G22:b::2::2:l:3:f:7\ncell:A23:b::2:::l:3:f:7\ncell:B23:b::2::2:l:3:f:7:cf:2\ncell:C23:b::1::1:f:1:cf:2:colspan:3\ncell:D23:b::1:::colspan:2\ncell:F23:b::1:::f:1:ntvf:1\ncell:G23:b::2::2:l:3:f:7\ncell:A24:b::2:::l:3:f:7\ncell:B24:b::2::2:l:3:f:7:cf:2\ncell:C24:b::1::1:f:1:cf:2:colspan:3\ncell:D24:b::1:::colspan:2\ncell:F24:b::1:::f:1:ntvf:1\ncell:G24:b::2::2:l:3:f:7\ncell:A25:b::2:::l:3:f:7\ncell:B25:b::2::2:l:3:f:7:cf:2\ncell:C25:b::1::1:f:1:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:1:ntvf:1\ncell:G25:b::2::2:l:3:f:7\ncell:A26:b::2:::l:3:f:7\ncell:B26:b::2::2:l:3:f:7:cf:2\ncell:C26:b::1::1:f:1:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:1:ntvf:1\ncell:G26:b::2::2:l:3:f:7\ncell:A27:b::2:::l:3:f:7\ncell:B27:b::2::2:l:3:f:7:cf:2\ncell:C27:b::1::1:f:1:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:1:ntvf:1\ncell:G27:b::2::2:l:3:f:7\ncell:A28:b::2:::l:3:f:4\ncell:B28:b::2::2:l:3:f:7:cf:2\ncell:C28:b::1:1:1:f:1:cf:2:colspan:3\ncell:D28:b::1:1::l:3:f:7:cf:2:colspan:2\ncell:E28:b:::2::l:3:f:7:cf:2\ncell:F28:b::1:1::f:1:ntvf:1\ncell:G28:b::2::2:l:3:f:7\ncell:A29:b::2:::l:3:f:3\ncell:B29:b::2::2:l:3:f:3\ncell:C29:t:TOTAL:b:1:1:1:1:f:5:cf:2:colspan:3\ncell:D29:b:2::2::l:3:f:8:cf:2\ncell:E29:b:2:2:2::l:3:f:8:cf:2\ncell:F29:vtf:n:0:SUM(F16\\cF28):b:1:1:1::f:5:ntvf:1\ncell:G29:b::2::2:l:3:f:4\ncell:A30:b::2:::l:3:f:7\ncell:B30:b::::2:l:3:f:3:cf:2\ncell:C30:b:2::::l:1:f:7\ncell:D30:b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:7\ncell:F30:b:2::::l:1:f:7\ncell:G30:b::2:::l:3:f:3\ncell:A31:b::2:::l:3:f:7\ncell:B31:b::::2:l:3:f:7\ncell:D31:tvf:4:rowspan:4\ncell:G31:b::2:::l:3:f:7\ncell:A32:b::2:::l:3:f:7\ncell:B32:b::::2:l:3:f:7\ncell:G32:b::2:::l:3:f:7\ncell:A33:b::2:::l:3:f:7\ncell:B33:b::::2:l:3:f:7\ncell:G33:b::2:::l:3:f:7\ncell:A34:b::2:::l:3:f:7\ncell:B34:b::::2:l:3:f:7\ncell:G34:b::2:::l:3:f:7\ncell:A35:b::2:::l:3:f:7\ncell:B35:b:::2:2:l:3:f:7\ncell:C35:b:::2::l:3:f:7\ncell:D35:b:::2::l:3:f:7\ncell:E35:b:::2::l:3:f:7\ncell:F35:b:::2::l:3:f:7\ncell:G35:b::2:2::l:3:f:7\ncell:B36:b:1:::\ncell:C36:b:1:::\ncell:D36:b:1:::\ncell:E36:b:1:::\ncell:F36:b:1:::\ncell:G36:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:105\ncol:D:w:182\ncol:E:w:110\ncol:F:w:115\ncol:G:w:65\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:15.75\nrow:29:h:15.75\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:7:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:normal bold * Trebuchet MS\nfont:3:normal bold 10pt Arial\nfont:4:normal bold 12pt Arial\nfont:5:normal bold 12pt Trebuchet MS\nfont:6:normal bold 14pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 12pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + }, + name: "typei", + hidden: "0", + }, + sheet2: { + sheetstr: { + savestr: + 'version:1.5\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE:b:1:1:1:1:l:3:f:6:cf:1:colspan:8\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:t::b:2::2::l:1:f:7\ncell:G2:b:2::2::l:1:f:7\ncell:H2:b:2::2::l:1:f:7\ncell:I2:t::b:2::2::l:1:f:7\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2:::2:l:3:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:2::::l:1:f:7\ncell:G3:b:2::::l:1:f:7\ncell:H3:b:2::::l:1:f:7\ncell:I3:b:2:2:::l:3:f:7\ncell:A4:b::2:::l:3:f:7\ncell:B4:b::::2:l:3:f:3\ncell:C4:t:INVOICE # \\c:f:2:cf:2\ncell:D4:v:1:f:2:cf:2\ncell:F4:tvf:5:colspan:2:rowspan:4\ncell:I4:b::2:::l:3:f:7\ncell:A5:b::2:::l:3:f:7\ncell:B5:b::::2:l:3:f:4\ncell:F5:t::l:2:f:7\ncell:G5:l:2:f:7\ncell:H5:l:2:f:7\ncell:I5:t::b::1:::l:2:f:7\ncell:A6:b::2:::l:3:f:7\ncell:B6:b::::2:l:3:f:4\ncell:C6:t:INVOICE DATE\\c:f:2:cf:2\ncell:D6:f:2:cf:2:ntvf:4\ncell:F6:l:2:f:7\ncell:G6:l:2:f:7\ncell:H6:l:2:f:7\ncell:I6:b::1:::l:2:f:7\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::::2:l:3:f:4\ncell:I7:b::2:::l:3:f:7\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::::2:l:3:f:4\ncell:I8:b::2:::l:3:f:7\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::::2:l:3:f:4\ncell:C9:t:BILL TO\\c:f:2\ncell:E9:t:FROM\\c:f:2:cf:2\ncell:F9:colspan:3\ncell:I9:b::2:::l:3:f:7\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::::2:l:3:f:3\ncell:C10:t:[Name]:f:1:cf:2:colspan:2\ncell:E10:t:[Name]:f:1:cf:2:colspan:4\ncell:I10:b::2:::l:3:f:7\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:E11:t:[Street Address]:f:1:colspan:4\ncell:I11:b::2:::l:3:f:7\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:E12:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:I12:b::2:::l:3:f:7\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:Phone\\c :f:1:cf:2:colspan:2\ncell:E13:t:Phone\\c :f:1:cf:2:colspan:4\ncell:I13:b::2:::l:3:f:7\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::::2:l:3:f:3:cf:2\ncell:C14:b:::2::l:1:f:7\ncell:D14:b:::2::l:3:f:3:cf:2\ncell:E14:b:::2::l:1:f:7\ncell:F14:b:::2::l:1:f:7\ncell:G14:b:::2::l:1:f:7\ncell:H14:b:::2::l:1:f:7\ncell:I14:b::2:::l:3:f:7\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::2::2:l:3:f:7\ncell:C15:t:Description:b:1:1:1:1:f:2:cf:1:colspan:3\ncell:D15:t:Description:b:2::2:2:l:3:f:3:cf:2:colspan:2:rowspan:1\ncell:E15:t::b:2:2:2::l:1:f:7\ncell:F15:t:Hours:b:1:1:1:1:f:2:cf:1\ncell:G15:t:Rate:b:1:1:1:1:f:2:cf:1\ncell:H15:t:Amount:b:1:1:1:1:f:2:cf:1\ncell:I15:b::2::2:l:3:f:7\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::2::2:l:3:f:7\ncell:C16:b:1:1::1:f:1:cf:2:colspan:3\ncell:D16:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:E16:t::b:2:2:2::l:1:f:7\ncell:F16:b:1:1:::f:1:ntvf:1\ncell:G16:b:1:1:::f:1:ntvf:1\ncell:H16:vtf:t::IF(F16*G16>0,F16*G16,""):b:1:1:::f:1:ntvf:1\ncell:I16:b::2::2:l:3:f:7\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::2::2:l:3:f:7:cf:2\ncell:C17:b::1::1:f:1:cf:2:colspan:3\ncell:D17:b::1::1:l:1:f:7:colspan:2\ncell:E17:l:1:f:7\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::1:::f:1:ntvf:1\ncell:H17:vtf:t::IF(F17*G17>0,F17*G17,""):b::1:::f:1:ntvf:1\ncell:I17:b::2::2:l:3:f:7\ncell:A18:b::2:::l:3:f:7\ncell:B18:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C18:b::1::1:f:1:cf:2:colspan:3\ncell:D18:b::1::1:colspan:2\ncell:F18:b::1:::f:1:ntvf:1\ncell:G18:b::1:::f:1:ntvf:1\ncell:H18:vtf:t::IF(F18*G18>0,F18*G18,""):b::1:::f:1:ntvf:1\ncell:I18:b::2::2:l:3:f:7\ncell:A19:b::2:::l:3:f:7\ncell:B19:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C19:b::1::1:f:1:cf:2:colspan:3\ncell:D19:b::1::1:colspan:2\ncell:F19:b::1:::f:1:ntvf:1\ncell:G19:b::1:::f:1:ntvf:1\ncell:H19:vtf:t::IF(F19*G19>0,F19*G19,""):b::1:::f:1:ntvf:1\ncell:I19:b::2::2:l:3:f:7\ncell:A20:b::2:::l:3:f:7\ncell:B20:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C20:b::1::1:f:1:cf:2:colspan:3\ncell:D20:b::1::1:colspan:2\ncell:F20:b::1:::f:1:ntvf:1\ncell:G20:b::1:::f:1:ntvf:1\ncell:H20:vtf:t::IF(F20*G20>0,F20*G20,""):b::1:::f:1:ntvf:1\ncell:I20:b::2::2:l:3:f:7\ncell:A21:b::2:::l:3:f:7\ncell:B21:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C21:b::1::1:f:1:cf:2:colspan:3\ncell:D21:b::1::1:colspan:2\ncell:F21:b::1:::f:1:ntvf:1\ncell:G21:b::1:::f:1:ntvf:1\ncell:H21:vtf:t::IF(F21*G21>0,F21*G21,""):b::1:::f:1:ntvf:1\ncell:I21:b::2::2:l:3:f:7\ncell:A22:b::2:::l:3:f:7\ncell:B22:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C22:b::1::1:f:1:cf:2:colspan:3\ncell:D22:b::1::1:colspan:2\ncell:F22:b::1:::f:1:ntvf:1\ncell:G22:b::1:::f:1:ntvf:1\ncell:H22:vtf:t::IF(F22*G22>0,F22*G22,""):b::1:::f:1:ntvf:1\ncell:I22:b::2::2:l:3:f:7\ncell:A23:b::2:::l:3:f:7\ncell:B23:b::2::2:l:3:f:7:cf:2\ncell:C23:b::1::1:f:1:cf:2:colspan:3\ncell:D23:b::1::1:colspan:2\ncell:F23:b::1:::f:1:ntvf:1\ncell:G23:b::1:::f:1:ntvf:1\ncell:H23:vtf:t::IF(F23*G23>0,F23*G23,""):b::1:::f:1:ntvf:1\ncell:I23:b::2::2:l:3:f:7\ncell:A24:b::2:::l:3:f:7\ncell:B24:b::2::2:l:3:f:7:cf:2\ncell:C24:b::1::1:f:1:cf:2:colspan:3\ncell:D24:b::1::1:colspan:2\ncell:F24:b::1:::f:1:ntvf:1\ncell:G24:b::1:::f:1:ntvf:1\ncell:H24:vtf:t::IF(F24*G24>0,F24*G24,""):b::1:::f:1:ntvf:1\ncell:I24:b::2::2:l:3:f:7\ncell:A25:b::2:::l:3:f:7\ncell:B25:b::2::2:l:3:f:7:cf:2\ncell:C25:b::1::1:f:1:cf:2:colspan:3\ncell:D25:b::1::1:colspan:2\ncell:F25:b::1:::f:1:ntvf:1\ncell:G25:b::1:::f:1:ntvf:1\ncell:H25:vtf:t::IF(F25*G25>0,F25*G25,""):b::1:::f:1:ntvf:1\ncell:I25:b::2::2:l:3:f:7\ncell:A26:b::2:::l:3:f:7\ncell:B26:b::2::2:l:3:f:7:cf:2\ncell:C26:b::1::1:f:1:cf:2:colspan:3\ncell:D26:b::1::1:colspan:2\ncell:F26:b::1:::f:1:ntvf:1\ncell:G26:b::1:::f:1:ntvf:1\ncell:H26:vtf:t::IF(F26*G26>0,F26*G26,""):b::1:::f:1:ntvf:1\ncell:I26:b::2::2:l:3:f:7\ncell:A27:b::2:::l:3:f:7\ncell:B27:b::2::2:l:3:f:7:cf:2\ncell:C27:b::1::1:f:1:cf:2:colspan:3\ncell:D27:b::1::1:colspan:2\ncell:F27:b::1:::f:1:ntvf:1\ncell:G27:b::1:::f:1:ntvf:1\ncell:H27:vtf:t::IF(F27*G27>0,F27*G27,""):b::1:::f:1:ntvf:1\ncell:I27:b::2::2:l:3:f:7\ncell:A28:b::2:::l:3:f:4\ncell:B28:b::2::2:l:3:f:7:cf:2\ncell:C28:b::1:1:1:f:1:cf:2:colspan:3\ncell:D28:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:E28:b:::2::l:3:f:7:cf:2\ncell:F28:b::1:1::f:1:ntvf:1\ncell:G28:b::1:1::f:1:ntvf:1\ncell:H28:vtf:t::IF(F28*G28>0,F28*G28,""):b::1:::f:1:ntvf:1\ncell:I28:b::2::2:l:3:f:7\ncell:A29:b::2:::l:3:f:3\ncell:B29:b::2::2:l:3:f:3\ncell:C29:t:TOTAL:b:1:1:1:1:l:3:f:5:cf:2:colspan:5\ncell:D29:b:2::2::l:3:f:8:cf:2\ncell:E29:b:2:2:2::l:3:f:8:cf:2\ncell:F29:vtf:n:0:SUM(F16\\cF28):b:2:2:2::l:3:f:4:ntvf:2\ncell:G29:b:2:2:2::l:3:f:4:ntvf:2\ncell:H29:vtf:n:0:SUM(H16\\cH28):b:1:1:1::f:5:ntvf:1\ncell:I29:b::2::2:l:3:f:4\ncell:A30:b::2:::l:3:f:7\ncell:B30:b::::2:l:3:f:3:cf:2\ncell:C30:b:2::::l:1:f:7\ncell:D30:b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:7\ncell:F30:b:2::::l:1:f:7\ncell:G30:b:2::::l:1:f:7\ncell:H30:b:2::::l:1:f:7\ncell:I30:b::2:::l:3:f:3\ncell:A31:b::2:::l:3:f:7\ncell:B31:b::::2:l:3:f:7\ncell:D31:tvf:5:rowspan:4\ncell:I31:b::2:::l:3:f:7\ncell:A32:b::2:::l:3:f:7\ncell:B32:b::::2:l:3:f:7\ncell:I32:b::2:::l:3:f:7\ncell:A33:b::2:::l:3:f:7\ncell:B33:b::::2:l:3:f:7\ncell:I33:b::2:::l:3:f:7\ncell:A34:b::2:::l:3:f:7\ncell:B34:b::::2:l:3:f:7\ncell:I34:b::2:::l:3:f:7\ncell:A35:b::2:::l:3:f:7\ncell:B35:b:::2:2:l:3:f:7\ncell:C35:b:::2::l:3:f:7\ncell:D35:b:::2::l:3:f:7\ncell:E35:b:::2::l:3:f:7\ncell:F35:b:::2::l:3:f:7\ncell:G35:b:::2::l:3:f:7\ncell:H35:b:::2::l:3:f:7\ncell:I35:b::2:2::l:3:f:7\ncell:B36:b:1:::\ncell:C36:b:1:::\ncell:D36:b:1:::\ncell:E36:b:1:::\ncell:F36:b:1:::\ncell:G36:b:1:::\ncell:H36:b:1:::\ncell:I36:b:1:::\ncol:A:w:26\ncol:B:w:28\ncol:C:w:96\ncol:D:w:203\ncol:E:w:51\ncol:F:w:50\ncol:G:w:58\ncol:H:w:80\ncol:I:w:28\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:15.75\nrow:29:h:15.75\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:9:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:normal bold * Trebuchet MS\nfont:3:normal bold 10pt Arial\nfont:4:normal bold 12pt Arial\nfont:5:normal bold 12pt Trebuchet MS\nfont:6:normal bold 14pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 12pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00;(#,##0.00)\nvalueformat:3:d-mmm\nvalueformat:4:m/d/yy\nvalueformat:5:text-html\n', + }, + name: "typeii", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "typei!C10": true, + "typei!B2": true, + "typei!C11": true, + "typei!C12": true, + "typei!C13": true, + "typei!D9": true, + "typei!D4": true, + "typei!D6": true, + "typei!E10": true, + "typei!E11": true, + "typei!E12": true, + "typei!E13": true, + "typei!F9": true, + "typei!C16": true, + "typei!C17": true, + "typei!C18": true, + "typei!C19": true, + "typei!C20": true, + "typei!C21": true, + "typei!C22": true, + "typei!C23": true, + "typei!C24": true, + "typei!C25": true, + "typei!C26": true, + "typei!C27": true, + "typei!C28": true, + "typei!F16": true, + "typei!F17": true, + "typei!F18": true, + "typei!F19": true, + "typei!F20": true, + "typei!F21": true, + "typei!F22": true, + "typei!F23": true, + "typei!F24": true, + "typei!F25": true, + "typei!F26": true, + "typei!F27": true, + "typei!F28": true, + "typei!E35": true, + "typeii!B2": true, + "typeii!D4": true, + "typeii!C10": true, + "typeii!C11": true, + "typeii!C12": true, + "typeii!C13": true, + "typeii!D9": true, + "typeii!E10": true, + "typeii!E11": true, + "typeii!E12": true, + "typeii!E13": true, + "typeii!F9": true, + "typeii!C16": true, + "typeii!C17": true, + "typeii!C18": true, + "typeii!C19": true, + "typeii!C20": true, + "typeii!C21": true, + "typeii!C22": true, + "typeii!C23": true, + "typeii!C24": true, + "typeii!C25": true, + "typeii!C26": true, + "typeii!C27": true, + "typeii!C28": true, + "typeii!F16": true, + "typeii!F17": true, + "typeii!F18": true, + "typeii!F19": true, + "typeii!F20": true, + "typeii!F21": true, + "typeii!F22": true, + "typeii!F23": true, + "typeii!F24": true, + "typeii!F25": true, + "typeii!F26": true, + "typeii!F27": true, + "typeii!F28": true, + "typeii!G16": true, + "typeii!G17": true, + "typeii!G18": true, + "typeii!G19": true, + "typeii!G20": true, + "typeii!G21": true, + "typeii!G22": true, + "typeii!G23": true, + "typeii!G24": true, + "typeii!G25": true, + "typeii!G26": true, + "typeii!G27": true, + "typeii!G28": true, + "typeii!E35": true, + "typeiii!G9": true, + "typeiii!G10": true, + "typeiii!B2": true, + "typeiii!B3": true, + "typeiii!B4": true, + "typeiii!B5": true, + "typeiii!B6": true, + "typeiii!B7": true, + "typeiii!B8": true, + "typeiii!B9": true, + "typeiii!B11": true, + "typeiii!B12": true, + "typeiii!B13": true, + "typeiii!B14": true, + "typeiii!B15": true, + "typeiii!B18": true, + "typeiii!B19": true, + "typeiii!B20": true, + "typeiii!B21": true, + "typeiii!B22": true, + "typeiii!B23": true, + "typeiii!B24": true, + "typeiii!B25": true, + "typeiii!B26": true, + "typeiii!B27": true, + "typeiii!B28": true, + "typeiii!B29": true, + "typeiii!G18": true, + "typeiii!G19": true, + "typeiii!G20": true, + "typeiii!G21": true, + "typeiii!G22": true, + "typeiii!G23": true, + "typeiii!G24": true, + "typeiii!G25": true, + "typeiii!G26": true, + "typeiii!G27": true, + "typeiii!G28": true, + "typeiii!G29": true, + "typeiii!B32": true, + "typeiii!B33": true, + "typeiii!B34": true, + "typeiii!G31": true, + "typeiii!G33": true, + "typeiii!B37": true, + "typeiv!G9": true, + "typeiv!G10": true, + "typeiv!B2": true, + "typeiv!B3": true, + "typeiv!B4": true, + "typeiv!B5": true, + "typeiv!B6": true, + "typeiv!B7": true, + "typeiv!B8": true, + "typeiv!B11": true, + "typeiv!B12": true, + "typeiv!B13": true, + "typeiv!B14": true, + "typeiv!B15": true, + "typeiv!B16": true, + "typeiv!B18": true, + "typeiv!B19": true, + "typeiv!B20": true, + "typeiv!B21": true, + "typeiv!B22": true, + "typeiv!B23": true, + "typeiv!B24": true, + "typeiv!B25": true, + "typeiv!B26": true, + "typeiv!B27": true, + "typeiv!B28": true, + "typeiv!B29": true, + "typeiv!E18": true, + "typeiv!E19": true, + "typeiv!E20": true, + "typeiv!E21": true, + "typeiv!E22": true, + "typeiv!E23": true, + "typeiv!E24": true, + "typeiv!E25": true, + "typeiv!E26": true, + "typeiv!E27": true, + "typeiv!E28": true, + "typeiv!E29": true, + "typeiv!F18": true, + "typeiv!F19": true, + "typeiv!F2": true, + "typeiv!F20": true, + "typeiv!F21": true, + "typeiv!F22": true, + "typeiv!F23": true, + "typeiv!F24": true, + "typeiv!F25": true, + "typeiv!F26": true, + "typeiv!F27": true, + "typeiv!F28": true, + "typeiv!F29": true, + "typeiv!B32": true, + "typeiv!B33": true, + "typeiv!B34": true, + "typeiv!G31": true, + "typeiv!G33": true, + "typeiv!B37": true, + "typeii!F15": true, + "typeii!G15": true, + "typeiv!E17": true, + "typeiv!F17": true, + "typeii!D6": true, + "typeiv!G11": true, + "typei!C4": true, + "typeii!C4": true, + "typeiii!F10": true, + "typeiv!F11": true, + "typeiv!F10": true, + "typeiii!F9": true, + "typeii!C6": true, + "typei!C6": true, + "typei!F4": true, + "typeii!F4": true, + "typeiii!F4": true, + "typeiii!F2": true, + "typeiv!F4": true, + "typei!F29": true, + "typeii!H29": true, + "typeiii!G30": true, + "typeiii!G34": true, + "typeiv!G30": true, + "typeiv!G34": true, + }, + constraints: {}, + }, + }, + footers: [ + { name: "Invoice 1", index: 1, isActive: true }, + { name: "Invoice 2", index: 2, isActive: false }, + ], + logoCell: null, + signatureCell: null, + cellMappings: {}, + }, + 4: { + template: "Web Invoice 2", + templateId: 4, + msc: { + numsheets: 2, + currentid: "sheet3", + currentname: "typeiii", + sheetArr: { + sheet3: { + sheetstr: { + savestr: + "version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:3:cf:2:colspan:3\ncell:C2:t::l:2:f:9\ncell:D2:t::l:2:f:9\ncell:E2:l:1:f:7:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:9\ncell:B3:t:[Company Slogan]:f:4:cf:2:colspan:3\ncell:C3:t::l:2:f:9\ncell:D3:t::l:2:f:9\ncell:B4:f:2:colspan:2\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:2\ncell:F5:l:1:f:6\ncell:G5:l:1:f:10:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:G6:l:1:f:9\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:2\ncell:B8:t:Email\\c:f:1:cf:2:colspan:2\ncell:B9:colspan:2\ncell:F9:t:DATE \\c:l:1:f:6:cf:2\ncell:G9:l:1:f:10:cf:2:ntvf:3\ncell:B10:t:BILL TO\\c:f:5:c:1:bg:3:cf:2:colspan:2\ncell:F10:t:INVOICE # \\c:l:1:f:6:cf:2\ncell:G10:v:1:l:1:f:10:cf:2\ncell:B11:t:[Name]:f:1:cf:2:colspan:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:2\ncell:F12:t: \ncell:B13:t:[Street Address]:f:1:cf:2:colspan:2\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:2\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:6:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C17:t::l:2:f:9\ncell:D17:t::l:2:f:9\ncell:E17:t::l:2:f:9\ncell:F17:t::l:2:f:9\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:6:c:1:bg:3:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C18:t::l:2:f:9\ncell:D18:t::l:2:f:9\ncell:E18:t::l:2:f:9\ncell:F18:t::b::2:::l:1:f:9\ncell:G18:b::1::1:f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C19:t::l:2:f:9\ncell:D19:t::l:2:f:9\ncell:E19:t::l:2:f:9\ncell:F19:t::b::2:::l:1:f:9\ncell:G19:b::1::1:f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C20:t::l:2:f:9\ncell:D20:t::l:2:f:9\ncell:E20:t::l:2:f:9\ncell:F20:t::b::2:::l:1:f:9\ncell:G20:b::1::1:f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C21:t::l:2:f:9\ncell:D21:t::l:2:f:9\ncell:E21:t::l:2:f:9\ncell:F21:t::b::2:::l:1:f:9\ncell:G21:b::1::1:f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C22:t::l:2:f:9\ncell:D22:t::l:2:f:9\ncell:E22:t::l:2:f:9\ncell:F22:t::b::2:::l:1:f:9\ncell:G22:b::1::1:f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:9\ncell:D23:t::l:2:f:9\ncell:E23:t::l:2:f:9\ncell:F23:t::b::2:::l:1:f:9\ncell:G23:b::1::1:f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:9\ncell:D24:t::l:2:f:9\ncell:E24:t::l:2:f:9\ncell:F24:t::b::2:::l:1:f:9\ncell:G24:b::1::1:f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:9\ncell:D25:t::l:2:f:9\ncell:E25:t::l:2:f:9\ncell:F25:t::b::2:::l:1:f:9\ncell:G25:b::1::1:f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:9\ncell:D26:t::l:2:f:9\ncell:E26:t::l:2:f:9\ncell:F26:t::b::2:::l:1:f:9\ncell:G26:b::1::1:f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:9\ncell:D27:t::l:2:f:9\ncell:E27:t::l:2:f:9\ncell:F27:t::b::2:::l:1:f:9\ncell:G27:b::1::1:f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:9\ncell:D28:t::l:2:f:9\ncell:E28:t::l:2:f:9\ncell:F28:t::b::2:::l:1:f:9\ncell:G28:b::1::1:f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:5:rowspan:1\ncell:C29:t::b:::2::l:1:f:9\ncell:D29:t::b:::2::l:1:f:9\ncell:E29:t::b:::2::l:1:f:9\ncell:F29:t::b::2:2::l:1:f:9\ncell:G29:b::1:1:1:f:1:ntvf:1\ncell:B30:b:2::::l:1:f:9\ncell:C30:b:2::::l:1:f:9\ncell:D30:b:2::::l:1:f:9\ncell:E30:b:2::::l:1:f:10\ncell:F30:t:Subtotal:b:2::::l:1:f:10\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:8:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:6:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:9\ncell:D31:t::b:::2::l:1:f:9\ncell:F31:t:Tax Rate:l:1:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3\ncell:C32:t::b:2::::l:1:f:9\ncell:D32:t::b:2::::l:1:f:9\ncell:F32:t:Tax:l:1:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3\ncell:C33:t::l:2:f:9\ncell:D33:t::l:2:f:9\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:9\ncell:D34:t::l:2:f:9\ncell:F34:t:TOTAL:b:2::::l:1:f:6\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:5:ntvf:1\ncell:C36:tvf:4:colspan:5:rowspan:7\ncol:A:w:40\ncol:B:w:232\ncol:C:w:53\ncol:D:w:90\ncol:E:w:54\ncol:F:w:91\ncol:G:w:99\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nsheet:c:7:r:36:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 16pt Trebuchet MS\nfont:4:italic normal * Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 28pt Trebuchet MS\nfont:8:normal normal * Trebuchet MS\nfont:9:normal normal 10pt Arial\nfont:10:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + }, + name: "typeiii", + hidden: "0", + }, + sheet4: { + sheetstr: { + savestr: + 'version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:2:cf:2:colspan:3:rowspan:1\ncell:C2:t::l:2:f:7\ncell:D2:t::l:2:f:7\ncell:F2:t:INVOICE:l:1:f:6:c:1:cf:2:colspan:2\ncell:G2:t::l:2:f:7\ncell:B3:t:[Company slogan]:f:3:cf:2:colspan:3:rowspan:1\ncell:C3:t::l:2:f:7\ncell:D3:t::l:2:f:7\ncell:B4:cf:2:colspan:3:rowspan:1\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:F5:l:1:f:5\ncell:G5:l:1:f:8:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:G6:l:1:f:7\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B8:t:Email\\c:f:1:cf:2:colspan:3:rowspan:1\ncell:B9:cf:2:colspan:3:rowspan:1\ncell:B10:t:BILL TO\\c:l:1:f:5:bg:2:cf:2:colspan:2\ncell:F10:t:DATE\\c:l:1:f:5:cf:2\ncell:G10:l:1:f:8:cf:2:ntvf:3\ncell:B11:t:[Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:F11:t:INVOICE # \\c:l:1:f:5:cf:2\ncell:G11:v:1:l:1:f:8:cf:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:J12:tvf:4\ncell:B13:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B16:cf:2:colspan:3:rowspan:1\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:5:bg:2:cf:1:colspan:3:rowspan:1\ncell:C17:t::b:1::1::l:1:f:7:bg:2\ncell:D17:t::b:1::1::l:1:f:7:bg:2\ncell:E17:t:HOURS:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:F17:t:RATE:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C18:t::b:2::::l:1:f:7\ncell:D18:t::b:2:2:::l:1:f:7\ncell:E18:b:1:1::1:f:1:ntvf:1\ncell:F18:b:1:1::1:f:1:ntvf:1\ncell:G18:vtf:t::IF(E18*F18>0,E18*F18,""):b:1:1:::f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C19:t::l:2:f:7\ncell:D19:t::b::2:::l:1:f:7\ncell:E19:b::1::1:f:1:ntvf:1\ncell:F19:b::1::1:f:1:ntvf:1\ncell:G19:vtf:t::IF(E19*F19>0,E19*F19,""):b::1:::f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C20:t::l:2:f:7\ncell:D20:t::b::2:::l:1:f:7\ncell:E20:b::1::1:f:1:ntvf:1\ncell:F20:b::1::1:f:1:ntvf:1\ncell:G20:vtf:t::IF(E20*F20>0,E20*F20,""):b::1:::f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C21:t::l:2:f:7\ncell:D21:t::b::2:::l:1:f:7\ncell:E21:b::1::1:f:1:ntvf:1\ncell:F21:b::1::1:f:1:ntvf:1\ncell:G21:vtf:t::IF(E21*F21>0,E21*F21,""):b::1:::f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C22:t::l:2:f:7\ncell:D22:t::b::2:::l:1:f:7\ncell:E22:b::1::1:f:1:ntvf:1\ncell:F22:b::1::1:f:1:ntvf:1\ncell:G22:vtf:t::IF(E22*F22>0,E22*F22,""):b::1:::f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C23:t::l:2:f:7\ncell:D23:t::b::2:::l:1:f:7\ncell:E23:b::1::1:f:1:ntvf:1\ncell:F23:b::1::1:f:1:ntvf:1\ncell:G23:vtf:t::IF(E23*F23>0,E23*F23,""):b::1:::f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C24:t::l:2:f:7\ncell:D24:t::b::2:::l:1:f:7\ncell:E24:b::1::1:f:1:ntvf:1\ncell:F24:b::1::1:f:1:ntvf:1\ncell:G24:vtf:t::IF(E24*F24>0,E24*F24,""):b::1:::f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C25:t::l:2:f:7\ncell:D25:t::b::2:::l:1:f:7\ncell:E25:b::1::1:f:1:ntvf:1\ncell:F25:b::1::1:f:1:ntvf:1\ncell:G25:vtf:t::IF(E25*F25>0,E25*F25,""):b::1:::f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C26:t::l:2:f:7\ncell:D26:t::b::2:::l:1:f:7\ncell:E26:b::1::1:f:1:ntvf:1\ncell:F26:b::1::1:f:1:ntvf:1\ncell:G26:vtf:t::IF(E26*F26>0,E26*F26,""):b::1:::f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C27:t::l:2:f:7\ncell:D27:t::b::2:::l:1:f:7\ncell:E27:b::1::1:f:1:ntvf:1\ncell:F27:b::1::1:f:1:ntvf:1\ncell:G27:vtf:t::IF(E27*F27>0,E27*F27,""):b::1:::f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C28:t::l:2:f:7\ncell:D28:t::b::2:::l:1:f:7\ncell:E28:b::1::1:f:1:ntvf:1\ncell:F28:b::1::1:f:1:ntvf:1\ncell:G28:vtf:t::IF(E28*F28>0,E28*F28,""):b::1:::f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:3:rowspan:1\ncell:C29:t::b:::2::l:1:f:7\ncell:D29:t::b::2:2::l:1:f:7\ncell:E29:b::1:1:1:f:1:ntvf:1\ncell:F29:b::1:1:1:f:1:ntvf:1\ncell:G29:vtf:t::IF(E29*F29>0,E29*F29,""):b::1:::f:1:ntvf:1\ncell:B30:b:2::::l:1:f:8:cf:1:colspan:3:rowspan:1\ncell:C30:t::b:2::::l:1:f:7\ncell:D30:t::b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:8\ncell:F30:t:Subtotal:b:1::::f:1\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:1:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:5:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:7\ncell:D31:t::b:::2::l:1:f:7\ncell:F31:t:Tax Rate:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3:rowspan:1\ncell:C32:t::b:2::::l:1:f:7\ncell:D32:t::b:2::::l:1:f:7\ncell:F32:t:Tax:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3:rowspan:1\ncell:C33:t::l:2:f:7\ncell:D33:t::l:2:f:7\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:7\ncell:D34:t::l:2:f:7\ncell:F34:t:TOTAL:b:1::::l:1:f:4\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:4:ntvf:1\ncell:B35:b:2::::l:1:f:7\ncell:C35:b:2::::l:1:f:7\ncell:D35:b:2::::l:1:f:7\ncell:C36:tvf:4:colspan:5:rowspan:7\ncol:A:w:40\ncol:B:w:194\ncol:C:w:128\ncol:D:w:60\ncol:E:w:65\ncol:F:w:95\ncol:G:w:90\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:10:r:36:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncolor:1:rgb(0,0,0)\ncolor:2:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 16pt Trebuchet MS\nfont:3:italic normal * Trebuchet MS\nfont:4:normal bold * Trebuchet MS\nfont:5:normal bold 10pt Trebuchet MS\nfont:6:normal bold 28pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', + }, + name: "typeiv", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "typei!C10": true, + "typei!B2": true, + "typei!C11": true, + "typei!C12": true, + "typei!C13": true, + "typei!D9": true, + "typei!D4": true, + "typei!D6": true, + "typei!E10": true, + "typei!E11": true, + "typei!E12": true, + "typei!E13": true, + "typei!F9": true, + "typei!C16": true, + "typei!C17": true, + "typei!C18": true, + "typei!C19": true, + "typei!C20": true, + "typei!C21": true, + "typei!C22": true, + "typei!C23": true, + "typei!C24": true, + "typei!C25": true, + "typei!C26": true, + "typei!C27": true, + "typei!C28": true, + "typei!F16": true, + "typei!F17": true, + "typei!F18": true, + "typei!F19": true, + "typei!F20": true, + "typei!F21": true, + "typei!F22": true, + "typei!F23": true, + "typei!F24": true, + "typei!F25": true, + "typei!F26": true, + "typei!F27": true, + "typei!F28": true, + "typei!E35": true, + "typeii!B2": true, + "typeii!D4": true, + "typeii!C10": true, + "typeii!C11": true, + "typeii!C12": true, + "typeii!C13": true, + "typeii!D9": true, + "typeii!E10": true, + "typeii!E11": true, + "typeii!E12": true, + "typeii!E13": true, + "typeii!F9": true, + "typeii!C16": true, + "typeii!C17": true, + "typeii!C18": true, + "typeii!C19": true, + "typeii!C20": true, + "typeii!C21": true, + "typeii!C22": true, + "typeii!C23": true, + "typeii!C24": true, + "typeii!C25": true, + "typeii!C26": true, + "typeii!C27": true, + "typeii!C28": true, + "typeii!F16": true, + "typeii!F17": true, + "typeii!F18": true, + "typeii!F19": true, + "typeii!F20": true, + "typeii!F21": true, + "typeii!F22": true, + "typeii!F23": true, + "typeii!F24": true, + "typeii!F25": true, + "typeii!F26": true, + "typeii!F27": true, + "typeii!F28": true, + "typeii!G16": true, + "typeii!G17": true, + "typeii!G18": true, + "typeii!G19": true, + "typeii!G20": true, + "typeii!G21": true, + "typeii!G22": true, + "typeii!G23": true, + "typeii!G24": true, + "typeii!G25": true, + "typeii!G26": true, + "typeii!G27": true, + "typeii!G28": true, + "typeii!E35": true, + "typeiii!G9": true, + "typeiii!G10": true, + "typeiii!B2": true, + "typeiii!B3": true, + "typeiii!B4": true, + "typeiii!B5": true, + "typeiii!B6": true, + "typeiii!B7": true, + "typeiii!B8": true, + "typeiii!B9": true, + "typeiii!B11": true, + "typeiii!B12": true, + "typeiii!B13": true, + "typeiii!B14": true, + "typeiii!B15": true, + "typeiii!B18": true, + "typeiii!B19": true, + "typeiii!B20": true, + "typeiii!B21": true, + "typeiii!B22": true, + "typeiii!B23": true, + "typeiii!B24": true, + "typeiii!B25": true, + "typeiii!B26": true, + "typeiii!B27": true, + "typeiii!B28": true, + "typeiii!B29": true, + "typeiii!G18": true, + "typeiii!G19": true, + "typeiii!G20": true, + "typeiii!G21": true, + "typeiii!G22": true, + "typeiii!G23": true, + "typeiii!G24": true, + "typeiii!G25": true, + "typeiii!G26": true, + "typeiii!G27": true, + "typeiii!G28": true, + "typeiii!G29": true, + "typeiii!B32": true, + "typeiii!B33": true, + "typeiii!B34": true, + "typeiii!G31": true, + "typeiii!G33": true, + "typeiii!B37": true, + "typeiv!G9": true, + "typeiv!G10": true, + "typeiv!B2": true, + "typeiv!B3": true, + "typeiv!B4": true, + "typeiv!B5": true, + "typeiv!B6": true, + "typeiv!B7": true, + "typeiv!B8": true, + "typeiv!B11": true, + "typeiv!B12": true, + "typeiv!B13": true, + "typeiv!B14": true, + "typeiv!B15": true, + "typeiv!B16": true, + "typeiv!B18": true, + "typeiv!B19": true, + "typeiv!B20": true, + "typeiv!B21": true, + "typeiv!B22": true, + "typeiv!B23": true, + "typeiv!B24": true, + "typeiv!B25": true, + "typeiv!B26": true, + "typeiv!B27": true, + "typeiv!B28": true, + "typeiv!B29": true, + "typeiv!E18": true, + "typeiv!E19": true, + "typeiv!E20": true, + "typeiv!E21": true, + "typeiv!E22": true, + "typeiv!E23": true, + "typeiv!E24": true, + "typeiv!E25": true, + "typeiv!E26": true, + "typeiv!E27": true, + "typeiv!E28": true, + "typeiv!E29": true, + "typeiv!F18": true, + "typeiv!F19": true, + "typeiv!F2": true, + "typeiv!F20": true, + "typeiv!F21": true, + "typeiv!F22": true, + "typeiv!F23": true, + "typeiv!F24": true, + "typeiv!F25": true, + "typeiv!F26": true, + "typeiv!F27": true, + "typeiv!F28": true, + "typeiv!F29": true, + "typeiv!B32": true, + "typeiv!B33": true, + "typeiv!B34": true, + "typeiv!G31": true, + "typeiv!G33": true, + "typeiv!B37": true, + "typeii!F15": true, + "typeii!G15": true, + "typeiv!E17": true, + "typeiv!F17": true, + "typeii!D6": true, + "typeiv!G11": true, + "typei!C4": true, + "typeii!C4": true, + "typeiii!F10": true, + "typeiv!F11": true, + "typeiv!F10": true, + "typeiii!F9": true, + "typeii!C6": true, + "typei!C6": true, + "typei!F4": true, + "typeii!F4": true, + "typeiii!F4": true, + "typeiii!F2": true, + "typeiv!F4": true, + "typei!F29": true, + "typeii!H29": true, + "typeiii!G30": true, + "typeiii!G34": true, + "typeiv!G30": true, + "typeiv!G34": true, + }, + constraints: {}, + }, + }, + footers: [ + { name: "Company Invoice 1", index: 1, isActive: true }, + { name: "Company Invoice 2", index: 2, isActive: false }, + ], + logoCell: null, + signatureCell: null, + cellMappings: {}, + }, +}; diff --git a/src/templates-meta.ts b/src/templates-meta.ts index 090c4ce..431f848 100644 --- a/src/templates-meta.ts +++ b/src/templates-meta.ts @@ -11,4 +11,16 @@ export let tempMeta = [ ImageUri: "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", }, + { + name: "Web Invoice 1", + template_id: 3, + ImageUri: + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + }, + { + name: "Web Invoice 2", + template_id: 4, + ImageUri: + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + }, ]; diff --git a/src/templates.ts b/src/templates.ts index ef9714b..f4ebc37 100644 --- a/src/templates.ts +++ b/src/templates.ts @@ -32,14 +32,23 @@ export interface TemplateData { index: number; isActive: boolean; }[]; - logoCell: string | null; - signatureCell: string | null; + logoCell: string | { [footerIndex: number]: string }; + signatureCell: string | { [footerIndex: number]: string }; cellMappings: { - [headingName: string]: { - [cellName: string]: { - heading: string; - datatype: string; - }; + [footerIndex: number]: { + [fieldName: string]: + | string + | { [subField: string]: any } + | { + name?: string; + Range?: { + start: number; + end: number; + }; + Content?: { + [fieldName: string]: string; + }; + }; }; }; } @@ -304,9 +313,74 @@ export let DATA: { [key: number]: TemplateData } = { { name: "Detail2", index: 2, isActive: false }, { name: "Invoice", index: 3, isActive: false }, ], - logoCell: null, - signatureCell: null, - cellMappings: {}, + logoCell: { + 1: "", + 2: "", + 3: "F8", + }, + signatureCell: { + 1: "", + 2: "", + 3: "", + }, + cellMappings: { + 1: { + Heading: "B2", + Items: { + Range: { + start: 6, + end: 18, + }, + Content: { + Description: "B", + Hours: "E", + Rate: "F", + }, + }, + }, + 2: { + Heading: "B2", + Items: { + name: "Items", + Range: { + start: 6, + end: 18, + }, + Content: { + Description: "B", + Qty: "E", + Price: "F", + }, + }, + }, + 3: { + Heading: "B2", + Date: "G4", + InvoiceNumber: "B5", + From: { + CompanyName: "B8", + StreetAddress: "B9", + CityStateZip: "B10", + Phone: "B11", + Email: "B12", + }, + BillTo: { + Name: "B15", + CompanyName: "B16", + StreetAddress: "B17", + CityStateZip: "B18", + Phone: "B19", + Email: "B20", + }, + TaxPercentage: "G37", + OtherCharges: "G39", + Notes: { + 1: "B38", + 2: "B39", + 3: "B40", + }, + }, + }, }, 2: { template: "Mobile Invoice 2", @@ -539,8 +613,83 @@ export let DATA: { [key: number]: TemplateData } = { { name: "Invoice 1", index: 1, isActive: true }, { name: "Invoice 2", index: 2, isActive: false }, ], - logoCell: null, - signatureCell: null, - cellMappings: {}, + logoCell: { + 1: "F7", + 2: "F7", + }, + signatureCell: { + 1: "", + 2: "", + }, + cellMappings: { + 1: { + Heading: "B2", + Date: "G4", + InvoiceNumber: "C18", + From: { + Name: "C12", + StreetAddress: "C13", + CityStateZip: "C14", + Phone: "D15", + Email: "C16", + }, + BillTo: { + Name: "C5", + StreetAddress: "C6", + CityStateZip: "C7", + Phone: "C8", + Email: "C9", + }, + Items: { + name: "Items", + Range: { + start: 23, + end: 35, + }, + Content: { + Description: "C", + Amount: "F", + }, + }, + Notes: "B39", + }, + 2: { + Heading: "B2", + Date: "G4", + InvoiceNumber: "B5", + From: { + Name: "B8", + StreetAddress: "B9", + CityStateZip: "B10", + Phone: "B11", + Email: "B12", + }, + BillTo: { + Name: "B15", + StreetAddress: "B17", + CityStateZip: "B18", + Phone: "B19", + Email: "B20", + }, + Items: { + name: "Items", + Range: { + start: 23, + end: 35, + }, + Content: { + Description: "B", + Amount: "G", + }, + }, + TaxRate: "G37", + OtherCharges: "G39", + Notes: { + 1: "B38", + 2: "B39", + 3: "B40", + }, + }, + }, }, }; diff --git a/src/utils/dynamicFormManager.ts b/src/utils/dynamicFormManager.ts new file mode 100644 index 0000000..3ed49f0 --- /dev/null +++ b/src/utils/dynamicFormManager.ts @@ -0,0 +1,295 @@ +import { TemplateData } from "../templates"; + +export interface DynamicFormField { + label: string; + value: string; + type: "text" | "email" | "number" | "decimal" | "textarea"; + cellMapping: string; +} + +export interface DynamicFormSection { + title: string; + fields: DynamicFormField[]; + isItems?: boolean; + itemsConfig?: { + name: string; + range: { start: number; end: number }; + content: { [key: string]: string }; + }; +} + +export interface ProcessedFormData { + [sectionKey: string]: any; +} + +/** + * Utility class for managing dynamic form generation based on cell mappings + */ +export class DynamicFormManager { + /** + * Determines the field type based on the field label + * @param label The field label + * @returns The appropriate input type + */ + static getFieldType( + label: string + ): "text" | "email" | "number" | "decimal" | "textarea" { + const lowerLabel = label.toLowerCase(); + if (lowerLabel.includes("email")) return "email"; + if (lowerLabel.includes("number") || lowerLabel.includes("#")) + return "number"; + if ( + lowerLabel.includes("rate") || + lowerLabel.includes("amount") || + lowerLabel.includes("price") || + lowerLabel.includes("tax") || + lowerLabel.includes("hours") || + lowerLabel.includes("qty") || + lowerLabel.includes("quantity") + ) + return "decimal"; + if (lowerLabel.includes("notes") || lowerLabel.includes("description")) + return "textarea"; + return "text"; + } + + /** + * Generates form sections from cell mappings + * @param cellMappings The cell mappings object for a specific footer + * @returns Array of form sections + */ + static generateFormSections(cellMappings: any): DynamicFormSection[] { + if (!cellMappings) return []; + + const sections: DynamicFormSection[] = []; + + Object.entries(cellMappings).forEach(([key, value]) => { + if (key === "Items") { + // Special handling for Items + const itemsConfig = value as any; + sections.push({ + title: itemsConfig.name || "Items", + fields: [], + isItems: true, + itemsConfig: { + name: itemsConfig.name || "Items", + range: itemsConfig.Range || { start: 1, end: 10 }, + content: itemsConfig.Content || {}, + }, + }); + } else if (typeof value === "string") { + // Simple field mapping + sections.push({ + title: key, + fields: [ + { + label: key, + value: "", + type: this.getFieldType(key), + cellMapping: value, + }, + ], + }); + } else if (typeof value === "object" && value !== null) { + // Nested object - create a section with multiple fields + const fields: DynamicFormField[] = []; + + const processObject = (obj: any, prefix: string = "") => { + Object.entries(obj).forEach(([subKey, subValue]) => { + if (typeof subValue === "string") { + fields.push({ + label: prefix ? `${prefix} ${subKey}` : subKey, + value: "", + type: this.getFieldType(subKey), + cellMapping: subValue, + }); + } else if (typeof subValue === "object" && subValue !== null) { + processObject(subValue, subKey); + } + }); + }; + + processObject(value); + + if (fields.length > 0) { + sections.push({ + title: key, + fields, + }); + } + } + }); + + return sections; + } + + /** + * Initializes form data based on form sections + * @param sections The form sections + * @returns Initial form data object + */ + static initializeFormData(sections: DynamicFormSection[]): ProcessedFormData { + const formData: ProcessedFormData = {}; + + sections.forEach((section) => { + if (section.isItems && section.itemsConfig) { + // Initialize items array + const itemsArray: any[] = []; + for ( + let i = section.itemsConfig.range.start; + i <= section.itemsConfig.range.end; + i++ + ) { + const item: any = {}; + Object.keys(section.itemsConfig.content).forEach((contentKey) => { + item[contentKey] = ""; + }); + itemsArray.push(item); + } + formData[section.title] = itemsArray; + } else { + // Initialize regular fields + const sectionData: any = {}; + section.fields.forEach((field) => { + sectionData[field.label] = ""; + }); + formData[section.title] = sectionData; + } + }); + + return formData; + } + + /** + * Validates form data + * @param formData The form data to validate + * @param sections The form sections for reference + * @returns Validation result with errors if any + */ + static validateFormData( + formData: ProcessedFormData, + sections: DynamicFormSection[] + ): { + isValid: boolean; + errors: string[]; + } { + const errors: string[] = []; + + sections.forEach((section) => { + if (section.isItems) { + const items = formData[section.title] as any[]; + if (items && items.length > 0) { + items.forEach((item, index) => { + Object.entries(item).forEach(([key, value]) => { + if ( + this.getFieldType(key) === "email" && + value && + !this.isValidEmail(value as string) + ) { + errors.push( + `Invalid email format in ${section.title} item ${ + index + 1 + }: ${key}` + ); + } + }); + }); + } + } else { + section.fields.forEach((field) => { + const value = formData[section.title]?.[field.label]; + if (field.type === "email" && value && !this.isValidEmail(value)) { + errors.push(`Invalid email format: ${field.label}`); + } + }); + } + }); + + return { + isValid: errors.length === 0, + errors, + }; + } + + /** + * Validates email format + * @param email The email to validate + * @returns Whether the email is valid + */ + private static isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + } + + /** + * Converts form data to spreadsheet format for cell mapping + * @param formData The form data + * @param sections The form sections + * @param footerIndex The active footer index + * @returns Object with cell references and values + */ + static convertToSpreadsheetFormat( + formData: ProcessedFormData, + sections: DynamicFormSection[], + footerIndex: number + ): { [cellRef: string]: any } { + const cellData: { [cellRef: string]: any } = {}; + + sections.forEach((section) => { + if (section.isItems && section.itemsConfig) { + // Handle items with range-based cell mapping + const items = formData[section.title] as any[]; + if (items && items.length > 0) { + items.forEach((item, index) => { + const rowNumber = section.itemsConfig!.range.start + index; + Object.entries(section.itemsConfig!.content).forEach( + ([fieldName, columnLetter]) => { + const cellRef = `${columnLetter}${rowNumber}`; + cellData[cellRef] = item[fieldName] || ""; + } + ); + }); + } + } else { + // Handle regular fields + section.fields.forEach((field) => { + const value = formData[section.title]?.[field.label]; + if (value && field.cellMapping) { + cellData[field.cellMapping] = value; + } + }); + } + }); + + return cellData; + } + + /** + * Gets the active footer from a template + * @param template The template data + * @returns The active footer or null + */ + static getActiveFooter(template: TemplateData) { + return ( + template.footers.find((footer) => footer.isActive) || + template.footers[0] || + null + ); + } + + /** + * Filters form sections based on footer index + * @param template The template data + * @param footerIndex The footer index to filter by + * @returns Array of form sections for the specified footer + */ + static getFormSectionsForFooter( + template: TemplateData, + footerIndex: number + ): DynamicFormSection[] { + const cellMappings = template.cellMappings[footerIndex]; + if (!cellMappings) return []; + + return this.generateFormSections(cellMappings); + } +} diff --git a/src/utils/templateManager.ts b/src/utils/templateManager.ts index d6bdf8c..86b2359 100644 --- a/src/utils/templateManager.ts +++ b/src/utils/templateManager.ts @@ -127,28 +127,37 @@ export class TemplateManager { static generateDefaultCellMappings( templateId: number ): TemplateMetadata["cellMappings"] { - // Default mappings based on common invoice patterns + // Default mappings based on footer index (0 = first footer, 1 = second footer, etc.) const defaultMappings: TemplateMetadata["cellMappings"] = { - "Company Information": { - B8: { heading: "Company Name", datatype: "text" }, - B9: { heading: "Street Address", datatype: "text" }, - B10: { heading: "City, State, Zip", datatype: "text" }, - B11: { heading: "Phone", datatype: "text" }, - B12: { heading: "Email", datatype: "email" }, + 0: { + "Company Name": "B8", + "Street Address": "B9", + City: "B10", + Phone: "B11", + Email: "B12", + "Invoice Number": "B5", + Date: "F4", + "Due Date": "G4", + "Customer Name": "B15", + "Customer Company": "B16", + "Customer Address": "B17", + "Customer Phone": "B19", + "Customer Email": "B20", }, - "Invoice Details": { - B2: { heading: "Invoice Title", datatype: "text" }, - B5: { heading: "Invoice Number", datatype: "text" }, - F4: { heading: "Date", datatype: "date" }, - G4: { heading: "Due Date", datatype: "date" }, - }, - "Bill To": { - B15: { heading: "Customer Name", datatype: "text" }, - B16: { heading: "Customer Company", datatype: "text" }, - B17: { heading: "Customer Address", datatype: "text" }, - B18: { heading: "Customer City, State, Zip", datatype: "text" }, - B19: { heading: "Customer Phone", datatype: "text" }, - B20: { heading: "Customer Email", datatype: "email" }, + 1: { + "Company Name": "B8", + "Street Address": "B9", + City: "B10", + Phone: "B11", + Email: "B12", + "Invoice Number": "B5", + Date: "F4", + "Due Date": "G4", + "Customer Name": "B15", + "Customer Company": "B16", + "Customer Address": "B17", + "Customer Phone": "B19", + "Customer Email": "B20", }, }; From cb59cdcf51eee4e47791a5c28cefe8fc80c5ccd8 Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Sat, 30 Aug 2025 03:42:35 +0530 Subject: [PATCH 5/9] update --- ARCHITECTURE_CHANGES.md | 86 -- ARCHITECTURE_DIAGRAMS.md | 266 ----- FEATURE_IMPLEMENTATION_SUMMARY.md | 89 -- IMPLEMENTATION_SUMMARY.md | 323 ----- IONALERT_IMPLEMENTATION.md | 115 -- ONBOARDING_TEST.md | 57 - TEMPLATE_MIGRATION_SUMMARY.md | 154 --- TEMPLATE_UI_IMPROVEMENTS.md | 88 -- TEMPLATE_UI_REDESIGN_SUMMARY.md | 120 -- TESTING.md | 403 ------- URL_FILE_EDITING.md | 116 -- index.html | 6 +- src/components/DynamicInvoiceForm.tsx | 8 +- src/components/Files/Files.css | 35 +- src/components/Files/Files.tsx | 340 +++--- src/components/Storage/LocalStorage.ts | 84 +- src/components/TemplateFiles.tsx | 31 +- src/contexts/InvoiceContext.tsx | 37 +- src/pages/FilesPage.css | 24 + src/pages/FilesPage.tsx | 1146 ++++++++++++++---- src/pages/Home.css | 6 +- src/pages/Home.tsx | 387 ++++-- src/pages/SettingsPage.tsx | 18 - src/template2.ts | 522 -------- src/templates-meta.ts | 65 +- src/templates-new.ts | 189 --- src/templates.ts | 1502 ++++++++++++++++-------- src/utils/dynamicFormManager.ts | 37 +- src/utils/templateInitializer.ts | 9 +- src/utils/templateManager.ts | 17 +- 30 files changed, 2692 insertions(+), 3588 deletions(-) delete mode 100644 ARCHITECTURE_CHANGES.md delete mode 100644 ARCHITECTURE_DIAGRAMS.md delete mode 100644 FEATURE_IMPLEMENTATION_SUMMARY.md delete mode 100644 IMPLEMENTATION_SUMMARY.md delete mode 100644 IONALERT_IMPLEMENTATION.md delete mode 100644 ONBOARDING_TEST.md delete mode 100644 TEMPLATE_MIGRATION_SUMMARY.md delete mode 100644 TEMPLATE_UI_IMPROVEMENTS.md delete mode 100644 TEMPLATE_UI_REDESIGN_SUMMARY.md delete mode 100644 TESTING.md delete mode 100644 URL_FILE_EDITING.md delete mode 100644 src/template2.ts delete mode 100644 src/templates-new.ts diff --git a/ARCHITECTURE_CHANGES.md b/ARCHITECTURE_CHANGES.md deleted file mode 100644 index ffd7e90..0000000 --- a/ARCHITECTURE_CHANGES.md +++ /dev/null @@ -1,86 +0,0 @@ -# Architecture Changes: Removed Default File Concept & Updated Navigation - -## Summary -Successfully restructured the application to remove the concept of default files and updated the navigation flow. The files page is now the main landing page, and users must always open a saved file to access the editor. - -## Key Changes Made - -### 1. App.tsx - Navigation Structure -- **Removed**: Tab bar navigation (`IonTabs`, `IonTabBar`, `IonTabButton`) -- **Updated**: Root route now redirects to `/app/files` instead of `/app/editor` -- **Simplified**: Routing structure to use regular `IonRouterOutlet` without tabs - -### 2. FilesPage.tsx - Main Landing Page -- **Added**: Settings button in the header for easy access -- **Removed**: Default file handling logic from `handleNewFileClick()` and `handleNewMedClick()` -- **Removed**: Unsaved changes alert (`showUnsavedChangesAlert`) -- **Removed**: `createNewFile()` and `createNewMed()` functions (replaced by template-specific creation) -- **Cleaned**: Removed `resetToDefaults` dependency - -### 3. Home.tsx (Editor Page) - File-Required Access -- **Added**: Back button in header pointing to files page -- **Added**: `useHistory` for navigation -- **Updated**: Initialization logic to require a selected file -- **Removed**: Default file creation and management -- **Added**: Redirect to files page if no file is selected or file doesn't exist -- **Updated**: Auto-save logic to handle only named files (removed default file handling) -- **Fixed**: Auto-save button visibility (now shows for any selected file) - -### 4. SettingsPage.tsx - Navigation Integration -- **Added**: Back button in header pointing to files page -- **Added**: `useHistory` for navigation -- **Added**: `arrowBack` icon import - -### 5. Files.tsx - File Management -- **Removed**: Default file exclusion filter -- **Simplified**: `handleSaveUnsavedChanges()` function (no longer handles default file) -- **Updated**: File validation to remove "default" file restriction - -### 6. Menu.tsx - Validation Updates -- **Updated**: File name validation to only restrict "Untitled" (removed "default" restriction) - -## User Flow Changes - -### Before -1. App loads → Default file opened in editor -2. Bottom tab navigation between Editor/Files/Settings -3. Default file automatically created and managed - -### After -1. App loads → Files page (main landing page) -2. User selects existing file OR creates new file with template -3. Editor opens with selected file -4. Back buttons navigate to files page -5. Settings accessible from files page header - -## Navigation Pattern - -``` -FilesPage (Main) - ├── SettingsPage (accessible via header button, has back button) - └── Home/Editor (accessible when file selected, has back button) -``` - -## Benefits - -1. **Cleaner UX**: Users must explicitly choose files to work with -2. **No Hidden State**: No invisible "default" file confusing users -3. **File-Centric**: App revolves around saved files, encouraging better file management -4. **Simplified Navigation**: Linear navigation instead of tab-based -5. **Mobile-Friendly**: Back button navigation pattern familiar to mobile users - -## Technical Impact - -- **Reduced Complexity**: Removed default file logic throughout the app -- **Better File Management**: All files are explicitly named and saved -- **Cleaner State Management**: No special handling for "default" vs named files -- **Improved Error Handling**: Clear redirects when files don't exist - -## Testing Recommendations - -1. Verify files page loads as default route -2. Test template selection and file creation flow -3. Verify back button navigation from editor and settings -4. Test auto-save functionality with named files -5. Ensure settings button works from files page -6. Verify proper handling when accessing editor without selected file diff --git a/ARCHITECTURE_DIAGRAMS.md b/ARCHITECTURE_DIAGRAMS.md deleted file mode 100644 index 3a1dbab..0000000 --- a/ARCHITECTURE_DIAGRAMS.md +++ /dev/null @@ -1,266 +0,0 @@ -# Multi-Template Architecture Diagram - -```mermaid -graph TB - subgraph "Application Layer" - App[App.tsx] - Pages[Pages Layer] - Components[Components Layer] - end - - subgraph "Template Management" - TI[TemplateInitializer] - TM[TemplateManager] - TD[templates.ts] - TMeta[templates-meta.ts] - end - - subgraph "Storage Layer" - LS[LocalStorage] - File[Enhanced File Class] - Preferences[Capacitor Preferences] - end - - subgraph "Template Data Structure" - T1[Template 1
Mobile Invoice 1] - T2[Template 2
Mobile Invoice 2] - TN[Template N
Custom Templates] - end - - subgraph "File Storage Strategy" - F1[template_1_file1.msc] - F2[template_1_file2.msc] - F3[template_2_file1.msc] - F4[template_2_file2.msc] - end - - subgraph "Metadata Structure" - Meta[TemplateMetadata] - Footers[Footers Array] - CellMap[Cell Mappings] - LogoCell[Logo Cell Reference] - SigCell[Signature Cell Reference] - end - - %% Initialization Flow - App -->|Initialize| TI - TI -->|Setup Default Metadata| TD - TI -->|Validate Templates| TD - TI -->|Create Registry| LS - - %% Template Management Flow - Pages -->|Template Operations| TM - TM -->|Extract Metadata| TD - TM -->|Filter Files| LS - - %% Storage Flow - Components -->|Save/Load Files| LS - LS -->|Enhanced File Creation| File - File -->|Store with Metadata| Preferences - - %% Template Isolation - T1 -->|Isolated Storage| F1 - T1 -->|Isolated Storage| F2 - T2 -->|Isolated Storage| F3 - T2 -->|Isolated Storage| F4 - - %% Metadata Flow - File -->|Contains| Meta - Meta -->|Includes| Footers - Meta -->|Includes| CellMap - Meta -->|Includes| LogoCell - Meta -->|Includes| SigCell - - %% Styling - classDef templateClass fill:#e1f5fe - classDef storageClass fill:#f3e5f5 - classDef metadataClass fill:#e8f5e8 - classDef fileClass fill:#fff3e0 - - class T1,T2,TN templateClass - class LS,File,Preferences storageClass - class Meta,Footers,CellMap,LogoCell,SigCell metadataClass - class F1,F2,F3,F4 fileClass -``` - -## Architecture Flow Diagram - -```mermaid -sequenceDiagram - participant User - participant App - participant TI as TemplateInitializer - participant TM as TemplateManager - participant LS as LocalStorage - participant Storage as Device Storage - - Note over User,Storage: Application Initialization - User->>App: Launch Application - App->>TI: Initialize Templates - TI->>TI: Validate Template Data - TI->>TI: Setup Default Metadata - TI->>LS: Create Template Registry - LS->>Storage: Store Registry - - Note over User,Storage: File Creation with Template - User->>App: Create New Invoice - App->>TM: Get Template Metadata - TM->>TI: Request Template Data - TI-->>TM: Return Template Metadata - TM-->>App: Enhanced File Creation - App->>LS: Save File with Metadata - LS->>Storage: Store File + Metadata - - Note over User,Storage: Template-Specific Operations - User->>App: Filter Files by Template - App->>LS: Get Files by Template ID - LS->>Storage: Query Template Files - Storage-->>LS: Return Filtered Files - LS-->>App: Template-Specific Files - App-->>User: Display Organized Files - - Note over User,Storage: Cross-Template Isolation - User->>App: Switch Template Type - App->>TM: Load Different Template - TM->>LS: Get Template Files - LS->>Storage: Isolated Storage Access - Storage-->>LS: Template-Isolated Data - LS-->>App: Clean Template Context - App-->>User: Isolated Template View -``` - -## Data Structure Diagram - -```mermaid -erDiagram - FILE { - string created - string modified - string name - string content - number billType - boolean isEncrypted - string password - TemplateMetadata templateMetadata - } - - TEMPLATE_METADATA { - string template - number templateId - Footer[] footers - string logoCell - string signatureCell - CellMappings cellMappings - } - - FOOTER { - string name - number index - boolean isActive - } - - CELL_MAPPINGS { - string headingName - CellDefinition[] cells - } - - CELL_DEFINITION { - string cellName - string heading - string datatype - } - - TEMPLATE_DATA { - string template - number templateId - MSC_DATA msc - Footer[] footers - string logoCell - string signatureCell - CellMappings cellMappings - } - - MSC_DATA { - number numsheets - string currentid - string currentname - SheetArray sheetArr - EditableCells EditableCells - } - - EDITABLE_CELLS { - boolean allow - CellReference[] cells - Constraints[] constraints - } - - FILE ||--|| TEMPLATE_METADATA : contains - TEMPLATE_METADATA ||--o{ FOOTER : has - TEMPLATE_METADATA ||--|| CELL_MAPPINGS : defines - CELL_MAPPINGS ||--o{ CELL_DEFINITION : contains - TEMPLATE_DATA ||--|| MSC_DATA : includes - TEMPLATE_DATA ||--o{ FOOTER : defines - MSC_DATA ||--|| EDITABLE_CELLS : configures -``` - -## Storage Isolation Diagram - -```mermaid -graph LR - subgraph "Template 1 Files" - T1F1[template_1_invoice1.msc] - T1F2[template_1_invoice2.msc] - T1F3[template_1_draft1.msc] - end - - subgraph "Template 2 Files" - T2F1[template_2_receipt1.msc] - T2F2[template_2_receipt2.msc] - T2F3[template_2_quote1.msc] - end - - subgraph "Template N Files" - TNF1[template_n_custom1.msc] - TNF2[template_n_custom2.msc] - end - - subgraph "Storage Operations" - Filter[Filter by Template] - Isolate[Template Isolation] - Organize[File Organization] - end - - subgraph "Benefits" - NoInterference[No Cross-Template Interference] - EasyManagement[Easy File Management] - ClearSeparation[Clear Separation of Concerns] - end - - T1F1 --> Filter - T1F2 --> Filter - T1F3 --> Filter - T2F1 --> Filter - T2F2 --> Filter - T2F3 --> Filter - TNF1 --> Filter - TNF2 --> Filter - - Filter --> Isolate - Isolate --> Organize - Organize --> NoInterference - Organize --> EasyManagement - Organize --> ClearSeparation - - %% Styling - classDef template1 fill:#ffebee - classDef template2 fill:#e8f5e8 - classDef templateN fill:#e1f5fe - classDef operation fill:#fff3e0 - classDef benefit fill:#f3e5f5 - - class T1F1,T1F2,T1F3 template1 - class T2F1,T2F2,T2F3 template2 - class TNF1,TNF2 templateN - class Filter,Isolate,Organize operation - class NoInterference,EasyManagement,ClearSeparation benefit -``` diff --git a/FEATURE_IMPLEMENTATION_SUMMARY.md b/FEATURE_IMPLEMENTATION_SUMMARY.md deleted file mode 100644 index 3219f62..0000000 --- a/FEATURE_IMPLEMENTATION_SUMMARY.md +++ /dev/null @@ -1,89 +0,0 @@ -# Multi-Template Feature Implementation Summary - -## Overview -Successfully implemented multi-template selection and metadata-aware autosave functionality in the Government Invoice Form application. - -## Key Features Implemented - -### 1. Template Selection in Files Component -- **Template Filtering**: Added dropdown to filter files by template type -- **Template Display**: Shows template information for each file -- **New File Creation**: Modal for creating new files with template selection -- **Visual Indicators**: Clear template identification in file listings - -### 2. Metadata-Aware Autosave in Home Page -- **Template Metadata**: All files now save with template metadata -- **Backward Compatibility**: Existing files without metadata are handled gracefully -- **Auto-Detection**: Template metadata is automatically extracted during save operations -- **Template Context**: Active template is preserved in file metadata - -### 3. Enhanced File Management -- **Template-Aware Storage**: Files are stored with comprehensive template metadata -- **Migration Ready**: System can identify and migrate legacy files -- **Data Integrity**: Template metadata includes all necessary template information - -## Technical Implementation - -### Files Component Updates (`src/components/Files/Files.tsx`) -```typescript -// Key additions: -- Template filtering state and UI -- Template selection in new file modal -- Template information display -- Template-aware file operations -``` - -### Home Page Updates (`src/pages/Home.tsx`) -```typescript -// Key additions: -- TemplateInitializer integration -- Metadata extraction during save -- Template-aware initialization -- Enhanced error handling -``` - -### Supporting Architecture -- **TemplateManager**: Utility for template operations -- **TemplateInitializer**: App initialization and metadata management -- **Enhanced File Class**: Template metadata support with backward compatibility - -## User Experience Improvements - -1. **Template Selection**: Users can easily filter and view files by template -2. **New File Creation**: Guided template selection when creating new files -3. **Template Awareness**: Clear indication of which template each file uses -4. **Seamless Migration**: Existing files continue to work without interruption - -## Data Structure -```typescript -interface TemplateMetadata { - template: string; - templateId: string; - footers: string[]; - logoCell: string | null; - signatureCell: string | null; - cellMappings: Record; -} -``` - -## Testing Recommendations - -1. **Template Filtering**: Verify filtering works correctly across all templates -2. **New File Creation**: Test file creation with different template selections -3. **Autosave**: Confirm template metadata is preserved during autosave -4. **Legacy Files**: Ensure existing files without metadata continue to function -5. **Template Switching**: Test switching between templates in the editor - -## Next Steps - -1. **Performance Testing**: Monitor performance with large numbers of files -2. **User Feedback**: Gather feedback on template selection UX -3. **Migration Utility**: Implement automatic migration for legacy files -4. **Template Management**: Consider adding template management features - -## Architecture Benefits - -- **Scalability**: Easy to add new templates without affecting existing ones -- **Maintainability**: Clear separation of template logic and file management -- **User Experience**: Intuitive template selection and file organization -- **Data Integrity**: Comprehensive metadata ensures template context is preserved diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md deleted file mode 100644 index 4a71238..0000000 --- a/IMPLEMENTATION_SUMMARY.md +++ /dev/null @@ -1,323 +0,0 @@ -# Multi-Template Architecture Implementation - -## 🎯 Overview - -This implementation introduces a comprehensive multi-template architecture for the Government Invoice Form application. The new system isolates MSC files by template type, preventing interference between different templates while maintaining full backward compatibility. - -## 🚀 Key Features - -### ✅ Template Isolation -- Each MSC file is associated with specific template metadata -- No cross-template interference -- Template-specific configurations are preserved -- Clean separation of concerns - -### ✅ Enhanced Metadata Management -- Rich template metadata structure -- Footer management per template -- Logo and signature cell references -- Flexible cell mapping system - -### ✅ Backward Compatibility -- Existing files continue to work without modification -- Gradual migration path -- No breaking changes to existing functionality -- Enhanced constructor supports both old and new signatures - -### ✅ Improved Organization -- Files organized by template type -- Easy filtering and management -- Template-specific operations -- Clear file categorization - -## 📁 New File Structure - -``` -src/ -├── components/ -│ ├── Storage/ -│ │ └── LocalStorage.ts # Enhanced with template metadata -│ └── TemplateFiles.tsx # Example multi-template component -├── utils/ -│ ├── templateManager.ts # Template utility functions -│ └── templateInitializer.ts # App initialization with templates -├── templates.ts # Enhanced template definitions -├── templates-meta.ts # Template metadata (existing) -└── App.tsx # Updated with template initialization -``` - -## 🔧 Implementation Details - -### Enhanced File Class - -The `File` class now includes template metadata while maintaining backward compatibility: - -```typescript -// NEW: With template metadata -const file = new File( - created, modified, content, name, billType, - templateMetadata, // TemplateMetadata object - isEncrypted, password -); - -// OLD: Still works (backward compatible) -const file = new File( - created, modified, content, name, billType, - isEncrypted, password -); -``` - -### Template Metadata Structure - -```typescript -interface TemplateMetadata { - template: string; // Template name - templateId: number; // Unique identifier - footers: Array<{ // Template-specific footers - name: string; - index: number; - isActive: boolean; - }>; - logoCell: string | null; // Logo cell reference - signatureCell: string | null; // Signature cell reference - cellMappings: { // Template-specific cell mappings - [headingName: string]: { - [cellName: string]: { - heading: string; - datatype: string; - }; - }; - }; -} -``` - -### Storage Strategy - -Files are now stored with template-specific keys: -``` -template_{templateId}_{fileName} -``` - -Examples: -- `template_1_invoice_001.msc` -- `template_2_receipt_001.msc` - -## 🎨 Usage Examples - -### 1. Initialize Template System - -```typescript -import { TemplateInitializer } from './utils/templateInitializer'; - -// App initialization -await TemplateInitializer.initializeApp(); -``` - -### 2. Create Template-Aware Files - -```typescript -import { TemplateInitializer } from './utils/templateInitializer'; -import { File } from './components/Storage/LocalStorage'; - -// Get template metadata -const metadata = TemplateInitializer.getTemplateMetadata(1); - -// Create new file with template -const file = new File( - new Date().toISOString(), - new Date().toISOString(), - mscContent, - "invoice.msc", - 1, - metadata -); -``` - -### 3. Filter Files by Template - -```typescript -import { TemplateManager } from './utils/templateManager'; - -// Get files for specific template -const template1Files = await local._getFilesByTemplate(1); - -// Filter existing files collection -const filteredFiles = TemplateManager.filterFilesByTemplate(allFiles, 1); -``` - -### 4. Work with Cell Mappings - -```typescript -// Generate default mappings -const defaultMappings = TemplateManager.generateDefaultCellMappings(1); - -// Merge additional mappings -const mergedMappings = TemplateManager.mergeCellMappings( - existingMappings, - additionalMappings -); -``` - -## 🔄 Migration Path - -### Phase 1: Current Implementation -- ✅ New architecture implemented -- ✅ Backward compatibility maintained -- ✅ Enhanced metadata structure -- ✅ Template isolation functionality - -### Phase 2: Gradual Enhancement (Future) -- Migrate existing files to new structure -- Enhanced UI for template management -- Advanced template customization - -### Phase 3: Full Optimization (Future) -- Complete transition to new architecture -- Performance optimizations -- Advanced template features - -## 🛠 Developer Guide - -### Adding New Templates - -1. **Define Template Data** (`src/templates.ts`): -```typescript -export let DATA = { - // ... existing templates - 3: { - template: "New Template Type", - templateId: 3, - msc: { /* MSC configuration */ }, - footers: [ - { name: "New Footer", index: 1, isActive: true } - ], - logoCell: null, - signatureCell: null, - cellMappings: { /* cell mappings */ } - } -}; -``` - -2. **Update Template Metadata** (`src/templates-meta.ts`): -```typescript -export let tempMeta = [ - // ... existing metadata - { - name: "New Template Type", - template_id: 3, - ImageUri: "base64_image_string" - } -]; -``` - -3. **Test Template Isolation**: -```typescript -// Verify new template works in isolation -const template3Files = await local._getFilesByTemplate(3); -``` - -### Extending Metadata - -1. **Update Interface**: -```typescript -interface TemplateMetadata { - // ... existing properties - newProperty: string; // Add new property -} -``` - -2. **Update Validation**: -```typescript -// In TemplateManager.validateMetadata() -return ( - // ... existing validations - typeof metadata.newProperty === 'string' -); -``` - -3. **Update Default Generation**: -```typescript -// In TemplateManager.generateDefaultCellMappings() -// Add handling for new property -``` - -## 📊 Benefits Achieved - -### 🎯 Template Isolation -- ✅ No interference between different template types -- ✅ Independent template configurations -- ✅ Clean separation of template-specific data - -### 📈 Improved Organization -- ✅ Files categorized by template -- ✅ Easy filtering and management -- ✅ Template-specific operations - -### 🔧 Enhanced Extensibility -- ✅ Easy addition of new templates -- ✅ Flexible metadata structure -- ✅ Template-specific customizations - -### 🔄 Backward Compatibility -- ✅ Existing files continue to work -- ✅ No breaking changes -- ✅ Gradual migration path - -### 💾 Robust Storage -- ✅ Self-contained file metadata -- ✅ No external dependencies -- ✅ Easy backup and restore - -## 🧪 Testing the Implementation - -### 1. Template Isolation Test -```typescript -// Create files with different templates -const file1 = new File(/* template 1 data */); -const file2 = new File(/* template 2 data */); - -// Verify isolation -const template1Files = await local._getFilesByTemplate(1); -const template2Files = await local._getFilesByTemplate(2); - -// Should only contain respective template files -assert(template1Files contains only template 1 files); -assert(template2Files contains only template 2 files); -``` - -### 2. Backward Compatibility Test -```typescript -// Old constructor should still work -const oldStyleFile = new File( - created, modified, content, name, billType, isEncrypted, password -); - -// Should have default metadata -assert(oldStyleFile.templateMetadata exists); -assert(oldStyleFile.templateMetadata.templateId === billType); -``` - -### 3. Metadata Validation Test -```typescript -const metadata = TemplateInitializer.getTemplateMetadata(1); -const isValid = TemplateManager.validateMetadata(metadata); -assert(isValid === true); -``` - -## 📚 Documentation - -- **[MULTI_TEMPLATE_ARCHITECTURE.md](./MULTI_TEMPLATE_ARCHITECTURE.md)** - Comprehensive architecture documentation -- **[ARCHITECTURE_DIAGRAMS.md](./ARCHITECTURE_DIAGRAMS.md)** - Visual diagrams and flowcharts -- **[TemplateFiles.tsx](./src/components/TemplateFiles.tsx)** - Example implementation component - -## 🎉 Conclusion - -The multi-template architecture successfully provides: - -1. **Complete template isolation** - Different MSC files no longer interfere with each other -2. **Enhanced organization** - Files are properly categorized and manageable by template -3. **Backward compatibility** - All existing functionality continues to work seamlessly -4. **Extensible design** - Easy to add new templates and enhance functionality -5. **Robust metadata system** - Rich template information stored with each file - -This implementation creates a solid foundation for scalable invoice template management while maintaining the reliability and functionality of the existing system. diff --git a/IONALERT_IMPLEMENTATION.md b/IONALERT_IMPLEMENTATION.md deleted file mode 100644 index 6b74bc7..0000000 --- a/IONALERT_IMPLEMENTATION.md +++ /dev/null @@ -1,115 +0,0 @@ -# IonAlert Implementation for File Creation - Summary - -## Change Made - -Successfully updated the FilesPage.tsx to use an IonAlert for file creation instead of an IonModal, making it consistent with the rename file functionality in Files.tsx. - -## Before vs After - -### Before (IonModal) -```tsx - - - - Create New File - - - - - - -
-
-

Template Info

-

Template Details

-
-
- File Name - -
-
- Cancel - Create -
-
-
-
-``` - -### After (IonAlert) -```tsx - { - // Handle file creation - }, - }, - ]} -/> -``` - -## Benefits of Using IonAlert - -1. **Consistency**: Now matches the exact style and behavior of the rename file alert -2. **Simplicity**: Much cleaner code with less boilerplate -3. **Native Feel**: IonAlert provides a more native mobile experience -4. **Smaller Bundle**: Removed unused modal-related imports and components -5. **Better UX**: Simpler, more focused interaction for users - -## Technical Changes - -### Removed Imports -- `IonCard`, `IonCardContent`, `IonCardHeader`, `IonCardTitle`, `IonCardSubtitle` -- `IonModal`, `IonInput`, `IonLabel` -- `add`, `close` icons - -### Kept Imports -- `IonAlert` for the new implementation -- Template-related icons (`chevronForward`, `chevronUp`, `chevronDown`, `layers`) - -### Alert Configuration -- **Header**: "Create New File" -- **Message**: Dynamic message based on selected template -- **Input**: Single text input for filename -- **Buttons**: Cancel and Create with proper handlers - -## User Experience - -The new implementation provides: -- Immediate focus on filename input -- Clear template context in the message -- Consistent button layout (Cancel | Create) -- Same look and feel as rename functionality -- Faster interaction with less UI complexity - -## Code Reduction - -Reduced the component from: -- ~50 lines of modal JSX -- Multiple component imports -- Complex layout management - -To: -- ~30 lines of alert configuration -- Minimal imports -- Simple, declarative structure - -This change makes the file creation flow more consistent with the existing rename functionality and provides a cleaner, more native user experience. diff --git a/ONBOARDING_TEST.md b/ONBOARDING_TEST.md deleted file mode 100644 index 18d0229..0000000 --- a/ONBOARDING_TEST.md +++ /dev/null @@ -1,57 +0,0 @@ -# User Onboarding Flow Test - -This document explains how to test the new user onboarding functionality. - -## How It Works - -### First Time User (New User) - -1. Visit the app for the first time -2. The landing page will be shown automatically -3. Click "Start Creating Invoices" or "Access Invoice Editor" button -4. User is redirected to `/app/editor` -5. The `isNewUser` flag is set to `false` in localStorage - -### Returning User (Existing User) - -1. Visit the app after completing onboarding -2. User is automatically redirected to `/app/editor` -3. Landing page is skipped - -### Testing the Reset Functionality - -1. Go to Settings page (`/app/settings`) -2. In the "Preferences" section, click "Reset Onboarding" -3. A success toast will appear: "Onboarding reset! Landing page will show on next visit." -4. Navigate to the home page (`/`) or refresh -5. Landing page will be shown again - -## Technical Implementation - -### Files Modified: - -1. `src/utils/helper.ts` - Added localStorage utility functions -2. `src/App.tsx` - Added conditional rendering logic -3. `src/pages/LandingPage.tsx` - Updated button handler to mark user as existing -4. `src/pages/SettingsPage.tsx` - Added reset onboarding option - -### LocalStorage Key: - -- Key: `invoiceApp_isNewUser` -- Values: - - `null` or `"true"` = New user (show landing page) - - `"false"` = Existing user (skip to editor) - -### API Functions: - -- `isNewUser()` - Returns boolean indicating if user is new -- `markUserAsExisting()` - Sets user as existing (called on button click) -- `resetUserOnboarding()` - Resets user to new status (for testing/reset) - -## User Flow: - -``` -First Visit -> Landing Page -> Click Button -> Set isNewUser=false -> Redirect to /app/editor -Next Visit -> Check isNewUser -> false -> Direct to /app/editor (skip landing) -Reset Option -> Click "Reset Onboarding" -> Set isNewUser=true -> Next visit shows landing -``` diff --git a/TEMPLATE_MIGRATION_SUMMARY.md b/TEMPLATE_MIGRATION_SUMMARY.md deleted file mode 100644 index 37cfa12..0000000 --- a/TEMPLATE_MIGRATION_SUMMARY.md +++ /dev/null @@ -1,154 +0,0 @@ -# Template Selection UI Migration - Implementation Summary - -## Overview -Successfully moved the template selection UI from the Files component to the FilesPage component, replacing the original "Create New Invoice" and "Medication Invoice" buttons with a more comprehensive template selection system. - -## Key Changes Made - -### 1. FilesPage.tsx - Enhanced Template Section - -#### Added Imports -- Added Ionic components: `IonButton`, `IonIcon`, `IonCard`, `IonCardContent`, etc. -- Added icons: `add`, `close`, `chevronForward`, `chevronUp`, `chevronDown`, `layers` -- Added template utilities: `tempMeta`, `TemplateInitializer` - -#### New State Management -```typescript -const [showAllTemplates, setShowAllTemplates] = useState(false); -const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); -const [selectedTemplateForFile, setSelectedTemplateForFile] = useState(null); -const [newFileName, setNewFileName] = useState(""); -``` - -#### Template Helper Functions -- `getAvailableTemplates()` - Gets all available templates -- `getTemplateInfo(templateId)` - Gets template display name -- `getTemplateMetadata(templateId)` - Gets template metadata from tempMeta -- `handleTemplateSelect(templateId)` - Handles template selection -- `createNewFileWithTemplate(templateId, fileName)` - Creates new file with selected template - -#### Enhanced Template Cards Display -- **Grid Layout**: Responsive grid with 280px minimum column width -- **Visual Template Cards**: Each card shows: - - Template image (80x80px) from base64 ImageUri - - Template name from metadata - - Footer count - - Template ID - - Forward arrow icon -- **Interactive Effects**: Hover animations with border color change and elevation -- **Progressive Disclosure**: Shows first 3 templates, expandable to show all -- **Expand/Collapse Button**: Smart button showing count of additional templates - -#### File Creation Flow -1. User clicks template card -2. Filename prompt modal appears with template context -3. User enters filename and clicks "Create File" -4. File created with template metadata and user navigated to editor - -### 2. Files.tsx - Simplified Component - -#### Removed Elements -- Template creation section with cards -- Filename prompt modal -- Template selection functions -- Unused state variables and imports - -#### Retained Elements -- File listing and management functionality -- Template filtering for existing files -- Search and sort capabilities -- File operations (edit, delete, rename) - -### 3. Template Integration Features - -#### Template Metadata Support -- Uses `templates-meta.ts` for template images and names -- Integrates with `TemplateInitializer` for template management -- Proper mapping between template IDs and metadata - -#### Enhanced User Experience -- **Visual Template Selection**: Images provide better template identification -- **Context-Aware Creation**: Shows selected template in filename prompt -- **Seamless Navigation**: Direct navigation to editor after file creation -- **Template Information**: Clear display of template details - -## User Experience Improvements - -### Before -- Two fixed buttons: "Create New Invoice" and "Medication Invoice" -- Limited template options -- Generic file creation process -- No visual template identification - -### After -- Dynamic template cards showing all available templates -- Visual template preview with images -- Template-specific information display -- Context-aware file creation -- Expandable template list for better organization - -## Technical Implementation Details - -### Template Card Structure -```tsx -
handleTemplateSelect(template.templateId)}> - {/* Template Image (80x80px) */} -
- -
- - {/* Template Information */} -
-

{metadata.name}

-

{template.footers.length} footer(s)

-

Template ID: {template.templateId}

-
- - {/* Navigation Icon */} - -
-``` - -### Filename Prompt Modal -- Context-aware title showing selected template -- Template name in subtitle -- Input validation -- Cancel and create actions -- Automatic cleanup on completion - -### Progressive Disclosure -- Shows first 3 templates by default -- "View X More Templates" button when applicable -- "Show Less" option when expanded -- Maintains clean interface while providing access to all templates - -## Benefits Achieved - -1. **Better Template Discovery**: All templates are visible and accessible -2. **Visual Template Identification**: Images help users identify templates quickly -3. **Scalable Design**: Easy to add new templates without UI changes -4. **Cleaner Architecture**: Template creation logic centralized in FilesPage -5. **Enhanced User Flow**: More intuitive template selection process -6. **Mobile-Friendly**: Responsive design works well on all screen sizes - -## File Structure Impact - -### Modified Files -- `src/pages/FilesPage.tsx` - Enhanced with template selection UI -- `src/components/Files/Files.tsx` - Simplified, focused on file management - -### Dependencies Used -- `src/templates-meta.ts` - Template metadata and images -- `src/utils/templateInitializer.ts` - Template management utilities -- Ionic React components for UI elements - -## Testing Recommendations - -1. **Template Display**: Verify all templates show with correct images and information -2. **File Creation**: Test complete flow from template selection to file creation -3. **Progressive Disclosure**: Test expand/collapse functionality -4. **Responsive Design**: Verify layout on different screen sizes -5. **Error Handling**: Test with invalid template data or missing metadata -6. **Navigation**: Ensure proper navigation to editor after file creation - -This migration successfully transforms the template selection experience from a static button-based approach to a dynamic, visual, and scalable template selection system that better serves user needs and provides a foundation for future template additions. diff --git a/TEMPLATE_UI_IMPROVEMENTS.md b/TEMPLATE_UI_IMPROVEMENTS.md deleted file mode 100644 index 095108c..0000000 --- a/TEMPLATE_UI_IMPROVEMENTS.md +++ /dev/null @@ -1,88 +0,0 @@ -# Template UI Improvements - Summary - -## Changes Made - -### 1. Removed Template ID from Template Cards -**Before:** -- Template cards showed "Template ID: X" -- Extra line of text cluttering the interface - -**After:** -- Cleaner template cards with just template name and footer count -- More professional and user-friendly appearance - -### 2. Simplified Create New File Modal - -**Before:** -- Complex modal with IonCard structure -- IonCardHeader, IonCardTitle, IonCardSubtitle components -- Bulky appearance with extra padding and structure -- "Create File" button with icon - -**After:** -- Simple, clean modal similar to rename file modal -- Direct content layout without card wrapper -- Centered title and subtitle information -- Streamlined button layout (Cancel | Create) -- Consistent with existing rename file modal design - -## Visual Improvements - -### Template Cards -``` -Old Layout: -┌─────────────────────────┐ -│ [IMG] Template Name │ -│ X footer(s) │ -│ Template ID: X │ ← Removed -└─────────────────────────┘ - -New Layout: -┌─────────────────────────┐ -│ [IMG] Template Name │ -│ X footer(s) │ -└─────────────────────────┘ -``` - -### Modal Layout -``` -Old Modal: -┌─────────────────────────┐ -│ Create New File [×] │ -├─────────────────────────┤ -│ ┌─────────────────────┐ │ -│ │ Card Header │ │ -│ │ ├─────────────────┤ │ │ -│ │ │ Card Content │ │ │ -│ │ │ Input Field │ │ │ -│ │ │ [Cancel][Create]│ │ │ -│ │ └─────────────────┘ │ │ -│ └─────────────────────┘ │ -└─────────────────────────┘ - -New Modal: -┌─────────────────────────┐ -│ Create New File [×] │ -├─────────────────────────┤ -│ Template Info │ -│ Input Field │ -│ [Cancel] [Create] │ -└─────────────────────────┘ -``` - -## Benefits - -1. **Cleaner Interface**: Removed unnecessary template ID reduces visual clutter -2. **Better Consistency**: Modal now matches the style of rename file modal -3. **Improved UX**: Simpler modal is easier to understand and interact with -4. **Professional Look**: Cleaner template cards look more polished -5. **Focus on Essentials**: Users see only the information they need (name, footer count) - -## User Impact - -- **Template Selection**: Users can focus on template name and functionality rather than technical IDs -- **File Creation**: Simplified modal reduces cognitive load during file creation -- **Visual Harmony**: Consistent modal design across the application -- **Mobile Friendly**: Simpler layout works better on smaller screens - -The changes maintain all functionality while providing a cleaner, more professional user interface that aligns with modern design principles. diff --git a/TEMPLATE_UI_REDESIGN_SUMMARY.md b/TEMPLATE_UI_REDESIGN_SUMMARY.md deleted file mode 100644 index 44e41f6..0000000 --- a/TEMPLATE_UI_REDESIGN_SUMMARY.md +++ /dev/null @@ -1,120 +0,0 @@ -# Template Selection UI Redesign - Implementation Summary - -## Overview -Successfully redesigned the Files component to replace the "Create New Invoice" and "Medication Invoice" buttons with direct template selection cards, creating a more intuitive and visual template selection experience. - -## Key Changes Implemented - -### 1. Template Cards Display -- **Direct Template Access**: Removed modal-based template selection in favor of immediate template cards display -- **Visual Template Cards**: Each template shows: - - Template image (from ImageUri in templates-meta.ts) - - Template name - - Footer count - - Template ID for mapping - - Interactive hover effects - -### 2. Smart Template Layout -- **Grid Layout**: Responsive grid showing up to 3 templates initially -- **Expand/Collapse**: "View More Templates" button for additional templates -- **Progressive Disclosure**: Cleaner interface showing most important templates first - -### 3. Streamlined File Creation Flow -- **One-Click Template Selection**: Clicking a template immediately starts file creation -- **Simple Filename Prompt**: Modal appears only for filename input -- **Context-Aware**: Shows selected template info in the filename prompt - -### 4. Enhanced Template Metadata Integration -- **Template Metadata**: Utilizes `templates-meta.ts` for template images and names -- **Image Display**: Properly renders template images from base64 ImageUri -- **Fallback Icons**: Shows layer icon when template image is not available - -## Technical Implementation Details - -### New State Variables -```typescript -const [showAllTemplates, setShowAllTemplates] = useState(false); -const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); -const [selectedTemplateForFile, setSelectedTemplateForFile] = useState(null); -``` - -### Helper Functions Added -```typescript -const getTemplateMetadata = (templateId: number) => { - return tempMeta.find(meta => meta.template_id === templateId); -}; - -const handleTemplateSelect = (templateId: number) => { - setSelectedTemplateForFile(templateId); - setNewFileTemplate(templateId); - setShowFileNamePrompt(true); -}; -``` - -### UI Components Structure -1. **Template Cards Section**: Grid layout with template information -2. **Expand/Collapse Control**: Smart button for additional templates -3. **Filename Prompt Modal**: Simplified modal for file naming -4. **Template Information Display**: Shows selected template context - -## User Experience Improvements - -### Before -- Multiple button clicks required (Create New → Select Template → Enter Name) -- Template selection hidden in modal -- Less visual template identification - -### After -- Single click to select template -- Visual template cards with images -- Immediate template information visibility -- Streamlined file creation process - -## Visual Design Features - -### Template Cards -- **Fixed Size Images**: 60x60px template preview images -- **Hover Effects**: Interactive feedback with border color changes -- **Information Display**: Template name, footer count, and ID -- **Responsive Grid**: Adapts to screen size - -### Smart Controls -- **Progressive Disclosure**: Show/hide additional templates -- **Clear Visual Hierarchy**: Template cards → Expand button → File list -- **Consistent Styling**: Matches existing design system - -## File Structure Updates - -### Modified Files -- `src/components/Files/Files.tsx` - Main implementation -- Added import for `templates-meta.ts` for template metadata - -### New Dependencies -- Utilizes existing `tempMeta` array for template images and names -- Integrates with existing `TemplateInitializer` for template management - -## Testing Recommendations - -1. **Template Display**: Verify all templates show with correct images and metadata -2. **File Creation Flow**: Test complete flow from template selection to file creation -3. **Responsive Behavior**: Check layout on different screen sizes -4. **Expand/Collapse**: Verify show more/less functionality works correctly -5. **Error Handling**: Test with missing template metadata or images - -## Future Enhancement Opportunities - -1. **Template Previews**: Add larger preview images or template previews -2. **Template Categories**: Group templates by type or purpose -3. **Recent Templates**: Show frequently used templates first -4. **Template Search**: Add search functionality for templates -5. **Template Management**: Allow users to customize or organize templates - -## Benefits Achieved - -- **Improved Discoverability**: Templates are immediately visible -- **Faster Workflow**: Reduced clicks for file creation -- **Better Visual Design**: Template cards provide better context -- **Scalable Architecture**: Easy to add more templates without cluttering UI -- **Mobile-Friendly**: Responsive design works well on mobile devices - -This redesign successfully transforms the template selection experience from a hidden, multi-step process to an intuitive, visual, and efficient workflow that better serves user needs. diff --git a/TESTING.md b/TESTING.md deleted file mode 100644 index 63dbbb6..0000000 --- a/TESTING.md +++ /dev/null @@ -1,403 +0,0 @@ -# Unit Tests Documentation - -This document provides comprehensive documentation for the unit tests in the Invoice Form project, including how to run tests, what they cover, and how to maintain them. - -## Overview - -The project uses **Vitest** as the testing framework with **@testing-library/react** for component testing. Tests are located in the `src/test/` directory and follow a structured approach to ensure comprehensive coverage of the invoice form functionality. - -## Test Structure - -``` -src/test/ -├── setup.ts # Test environment setup and mocks -├── components/ -│ ├── InvoiceForm.test.tsx # Invoice form component tests -│ └── invoice.test.ts # Invoice module logic tests -``` - -## Running Tests - -### Prerequisites - -Ensure you have all dependencies installed: - -```bash -npm install -``` - -### Running All Tests - -```bash -npm run test -``` - -### Running Tests in Watch Mode - -```bash -npm run test:watch -``` - -### Running Tests with Coverage - -```bash -npm run test:coverage -``` - -### Running Specific Test Files - -```bash -# Run only InvoiceForm component tests -npm run test -- InvoiceForm - -# Run only invoice module tests -npm run test -- invoice.test.ts -``` - -## Test Configuration - -### Vitest Configuration (`vitest.config.ts`) - -The test configuration includes: - -- **Environment**: `jsdom` for DOM testing -- **Setup Files**: `src/test/setup.ts` for global mocks and configuration -- **Coverage**: Configured to track coverage across source files -- **Globals**: Enables global test functions (describe, it, expect) - -### Test Setup (`src/test/setup.ts`) - -The setup file provides comprehensive mocks for: - -- **Ionic React Components**: All IonModal, IonButton, IonInput, etc. -- **SocialCalc**: Spreadsheet engine with mock functions -- **Capacitor APIs**: File system, device, network APIs -- **React Router**: Navigation and routing -- **Browser APIs**: localStorage, sessionStorage, File, Blob, etc. - -## Test Coverage - -### InvoiceForm Component Tests (`InvoiceForm.test.tsx`) - -#### Rendering Tests - -- ✅ **Renders when open**: Verifies the modal displays when `isOpen={true}` -- ✅ **Does not render when closed**: Ensures modal is hidden when `isOpen={false}` -- ✅ **Displays all form fields**: Checks presence of required input fields - -#### User Interaction Tests - -- ✅ **Updates form fields when user types**: Validates input handling -- ✅ **Clears form data when clear button is clicked**: Tests form reset functionality -- ✅ **Adds invoice data when add button is clicked**: Verifies data submission -- ✅ **Closes modal when close button is clicked**: Tests modal dismissal - -#### Validation Tests - -- ✅ **Shows validation error for missing required fields**: Ensures form validation -- ✅ **Resets form when modal is opened**: Verifies clean state on open - -#### Line Items Tests - -- ✅ **Supports adding line items**: Tests dynamic item addition -- ✅ **Handles line item calculations**: Verifies amount calculations - -### Invoice Module Tests (`invoice.test.ts`) - -#### addInvoiceData Function Tests - -- ✅ **Adds basic invoice information**: Tests header data insertion -- ✅ **Adds line items to sheet**: Verifies item array handling -- ✅ **Handles partial data gracefully**: Tests with incomplete data -- ✅ **Handles empty items array**: Edge case testing -- ✅ **Handles invalid input gracefully**: Null/undefined input testing - -#### clearInvoiceData Function Tests - -- ✅ **Clears all invoice data**: Verifies complete data removal -- ✅ **Handles empty sheet gracefully**: Edge case with no data -- ✅ **Preserves non-invoice data**: Ensures selective clearing - -#### Integration Tests - -- ✅ **Add and clear cycle**: Tests complete workflow -- ✅ **Multiple add operations**: Tests data overwriting behavior - -## Test Patterns and Best Practices - -### 1. Component Testing Pattern - -```typescript -describe('ComponentName', () => { - const defaultProps = { - // Define default props - }; - - beforeEach(() => { - // Reset mocks and state - vi.clearAllMocks(); - }); - - const renderWithProvider = (props = defaultProps) => { - return render( - - - - ); - }; - - it('should do something', () => { - // Test implementation - }); -}); -``` - -### 2. User Interaction Testing - -```typescript -it('updates form field when user types', async () => { - renderWithProvider(); - - const input = screen.getByPlaceholderText(/field name/i); - - fireEvent.change(input, { - target: { value: 'test value' } - }); - - await waitFor(() => { - expect(input).toHaveValue('test value'); - }); -}); -``` - -### 3. Mock Function Verification - -```typescript -it('calls function with correct parameters', async () => { - const mockFunction = vi.fn(); - - // Trigger action - fireEvent.click(button); - - await waitFor(() => { - expect(mockFunction).toHaveBeenCalledWith( - expect.objectContaining({ - expectedProperty: 'expectedValue', - }) - ); - }); -}); -``` - -## Mock Documentation - -### SocialCalc Mocks - -The SocialCalc global object is mocked with: - -```typescript -global.SocialCalc = { - SpreadsheetControl: class MockSpreadsheetControl { - // Mock spreadsheet control implementation - }, - GetCellContents: vi.fn(), - ParseSheetSave: vi.fn(), - CreateSheetSave: vi.fn(), - addInvoiceData: vi.fn(), - clearInvoiceData: vi.fn(), -}; -``` - -### Ionic Component Mocks - -All Ionic components are mocked to render as standard HTML elements: - -```typescript -IonModal: ({ children, isOpen, ...props }) => - isOpen ? React.createElement('div', { 'data-testid': 'ion-modal' }, children) : null -``` - -### Capacitor API Mocks - -Device APIs are mocked for testing in web environment: - -```typescript -vi.mock('@capacitor/filesystem', () => ({ - Filesystem: { - writeFile: vi.fn(() => Promise.resolve()), - readFile: vi.fn(() => Promise.resolve({ data: '' })), - // ... other methods - }, -})); -``` - -## Adding New Tests - -### 1. Component Tests - -When adding new components, create a test file following the pattern: - -```typescript -// src/test/components/NewComponent.test.tsx -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { describe, it, expect } from 'vitest'; -import NewComponent from '../../components/NewComponent'; - -describe('NewComponent', () => { - it('should render correctly', () => { - render(); - expect(screen.getByText('Expected Text')).toBeInTheDocument(); - }); -}); -``` - -### 2. Function/Module Tests - -For utility functions or modules: - -```typescript -// src/test/utils/utilFunction.test.ts -import { describe, it, expect } from 'vitest'; -import { utilFunction } from '../../utils/utilFunction'; - -describe('utilFunction', () => { - it('should return expected result', () => { - const result = utilFunction('input'); - expect(result).toBe('expected output'); - }); -}); -``` - -## Testing Guidelines - -### Do's ✅ - -- **Test user behavior**, not implementation details -- **Use descriptive test names** that explain what is being tested -- **Group related tests** using `describe` blocks -- **Reset mocks** between tests using `beforeEach` -- **Use `waitFor`** for asynchronous operations -- **Test error conditions** and edge cases -- **Mock external dependencies** consistently - -### Don'ts ❌ - -- **Don't test internal state** unless necessary -- **Don't test third-party libraries** functionality -- **Don't write overly complex tests** that are hard to understand -- **Don't forget to clean up** after tests -- **Don't hardcode values** that might change - -## Debugging Tests - -### 1. Debug Mode - -Run tests with debugging information: - -```bash -npm run test -- --reporter=verbose -``` - -### 2. Single Test Debugging - -Focus on a specific test: - -```typescript -it.only('should test specific behavior', () => { - // Test implementation -}); -``` - -### 3. Console Debugging - -Use `screen.debug()` to see rendered output: - -```typescript -it('should render something', () => { - render(); - screen.debug(); // Prints current DOM - // Test assertions -}); -``` - -### 4. Mock Debugging - -Log mock calls for debugging: - -```typescript -const mockFn = vi.fn(); -// ... trigger action -console.log('Mock calls:', mockFn.mock.calls); -``` - -## Continuous Integration - -Tests run automatically on: - -- **Pull requests**: All tests must pass -- **Main branch pushes**: Full test suite execution -- **Release builds**: Tests + coverage reporting - -### Coverage Requirements - -Maintain minimum coverage levels: - -- **Statements**: 80% -- **Branches**: 75% -- **Functions**: 80% -- **Lines**: 80% - -## Troubleshooting - -### Common Issues - -#### 1. "Cannot find module" errors - -Ensure all dependencies are installed and imports are correct. - -#### 2. "Document is not defined" errors - -Check that `jsdom` environment is configured in vitest.config.ts. - -#### 3. Mock not working - -Verify mock is defined before component import and uses correct module path. - -#### 4. Async test failures - -Use `waitFor` for DOM updates and `await` for async operations. - -### Getting Help - -If you encounter issues: - -1. Check the test output for specific error messages -2. Verify mock configurations in `setup.ts` -3. Review existing test patterns for reference -4. Check Vitest and Testing Library documentation - -## Maintenance - -### Regular Tasks - -- **Review test coverage** monthly and add tests for uncovered code -- **Update mocks** when adding new dependencies -- **Refactor tests** when components change significantly -- **Document new testing patterns** in this guide - -### Version Updates - -When updating testing dependencies: - -1. Update package.json versions -2. Test that existing tests still pass -3. Update mocks if APIs changed -4. Update this documentation if needed - -## Resources - -- [Vitest Documentation](https://vitest.dev/) -- [Testing Library React](https://testing-library.com/docs/react-testing-library/intro/) -- [Jest DOM Matchers](https://github.com/testing-library/jest-dom) -- [Ionic Testing Guide](https://ionicframework.com/docs/react/testing) diff --git a/URL_FILE_EDITING.md b/URL_FILE_EDITING.md deleted file mode 100644 index 48cf2e6..0000000 --- a/URL_FILE_EDITING.md +++ /dev/null @@ -1,116 +0,0 @@ -# URL-Based File Editing System - -## Summary -Implemented a dedicated URL structure for editing specific files with proper file existence validation and user-friendly error handling. - -## New URL Structure - -### Routes -- `/app/files` - File explorer (main landing page) -- `/app/editor` - Editor without specific file (redirects to files) -- `/app/editor/:fileName` - Editor with specific file -- `/app/settings` - Settings page - -### Examples -- `/app/editor/invoice-2024-01` - Edit file named "invoice-2024-01" -- `/app/editor/My%20Invoice` - Edit file named "My Invoice" (URL encoded) - -## Implementation Details - -### 1. App.tsx - Updated Routing -```tsx - - - - - - -``` - -### 2. Home.tsx - File Parameter Handling -- **URL Parameter Extraction**: Uses `useParams<{ fileName?: string }>()` to get filename from URL -- **File Existence Check**: Validates if the requested file exists in local storage -- **Context Synchronization**: Updates the invoice context if URL parameter differs from selected file -- **Error State**: Shows "File Not Found" UI when file doesn't exist - -### 3. FilesPage.tsx - Updated Navigation -- **File Creation**: Redirects to `/app/editor/${fileName}` after creating new files -- **URL Encoding**: Uses `encodeURIComponent()` for filenames with special characters - -### 4. Files.tsx - Updated File Opening -- **File Opening**: Navigates to `/app/editor/${fileName}` when opening existing files -- **URL Encoding**: Handles filenames with spaces and special characters - -## User Experience - -### File Not Found State -When accessing a non-existent file via URL: -- Shows a clean error message -- Displays file icon and "File Not Found" heading -- Provides clear explanation -- Shows "Go to File Explorer" button to redirect to `/app/files` - -### Navigation Flow -1. **Files Page**: User selects or creates a file -2. **URL Navigation**: App navigates to `/app/editor/{fileName}` -3. **File Loading**: Home component loads the specific file -4. **Error Handling**: If file doesn't exist, shows error with option to return to files - -## Benefits - -### 1. Direct File Access -- Users can bookmark specific files -- Share direct links to files -- Browser back/forward works correctly - -### 2. Better Error Handling -- Clear feedback when files don't exist -- Graceful fallback to file explorer -- No confusing redirects - -### 3. URL Consistency -- Predictable URL patterns -- RESTful-style resource access -- Better browser integration - -### 4. File Management -- URL reflects current file being edited -- Easy to see which file is active -- Better browser history - -## Technical Features - -### URL Encoding -- Handles filenames with spaces: `My File` → `My%20File` -- Supports special characters safely -- Decodes properly in component - -### State Management -- Synchronizes URL parameters with React context -- Updates selected file when URL changes -- Maintains consistency between URL and app state - -### Error Boundaries -- Validates file existence before loading -- Provides fallback UI for missing files -- Prevents crashes from invalid file access - -## Usage Examples - -### Direct File Access -``` -/app/editor/Invoice-Jan-2024 → Opens "Invoice-Jan-2024" -/app/editor/My%20Monthly%20Bill → Opens "My Monthly Bill" -``` - -### File Creation Flow -1. User clicks template in FilesPage -2. Enters filename: "Q1 Report" -3. App creates file and navigates to `/app/editor/Q1%20Report` -4. Editor opens with new file loaded - -### Error Handling -1. User visits `/app/editor/NonExistentFile` -2. Home component checks if "NonExistentFile" exists -3. File not found → Shows error UI -4. User clicks "Go to File Explorer" → Redirects to `/app/files` diff --git a/index.html b/index.html index da370c8..67d79da 100644 --- a/index.html +++ b/index.html @@ -19,9 +19,9 @@ - - - + + + diff --git a/src/components/DynamicInvoiceForm.tsx b/src/components/DynamicInvoiceForm.tsx index a6031db..3fa2548 100644 --- a/src/components/DynamicInvoiceForm.tsx +++ b/src/components/DynamicInvoiceForm.tsx @@ -49,7 +49,7 @@ interface DynamicInvoiceFormProps { } const DynamicInvoiceForm: React.FC = ({ isOpen, onClose }) => { - const { activeTempId } = useInvoice(); + const { activeTemplateData } = useInvoice(); const [activeFooterIndex, setActiveFooterIndex] = useState(1); const [formData, setFormData] = useState({}); const [showToast, setShowToast] = useState(false); @@ -58,8 +58,8 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose // Get current template data const currentTemplate = useMemo(() => { - return DATA[activeTempId]; - }, [activeTempId]); + return activeTemplateData; + }, [activeTemplateData]); // Get active footer based on activeFooterIndex const activeFooter = useMemo(() => { @@ -120,7 +120,7 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose // Create invoice data object const invoiceData = { - templateId: activeTempId, + templateId: activeTemplateData ? activeTemplateData.templateId : 1, footerIndex: activeFooterIndex, cellData, dynamicData: formData, diff --git a/src/components/Files/Files.css b/src/components/Files/Files.css index b361caf..8b9e304 100644 --- a/src/components/Files/Files.css +++ b/src/components/Files/Files.css @@ -115,12 +115,12 @@ background: #555; } -/* File Icon Styling - smaller for mobile */ +/* File Icon Styling - bigger for better visibility */ .file-icon { margin-right: 6px; - font-size: 1rem; + font-size: 1.5rem; color: var(--ion-color-medium); - min-width: 20px; + min-width: 28px; } /* Mobile-friendly file items */ @@ -173,6 +173,31 @@ margin-left: auto; } +/* Smaller template chips */ +.template-chip { + font-size: 0.6rem !important; + height: 20px !important; + --padding-start: 1px !important; + --padding-end: 1px !important; + --padding-top: 2px !important; + --padding-bottom: 2px !important; + min-height: 20px !important; + margin-left: 0 !important; + margin-right: 0 !important; +} + +.template-chip ion-label { + font-size: 0.6rem !important; + margin: 0 !important; + padding: 0 !important; +} + +.template-chip ion-icon { + font-size: 0.6rem !important; + margin-right: 2px !important; + margin-left: 0 !important; +} + /* Smaller font size for specific segment buttons */ .smaller-segment-text ion-segment-button { font-size: 0.75rem !important; @@ -772,8 +797,8 @@ ion-icon[slot="end"] { padding: 12px 16px; border: none; background: transparent; - font-size: 14px; - font-weight: 500; + font-size: 18px; + font-weight: 600; cursor: pointer; transition: all 0.2s ease; border-bottom: 2px solid transparent; diff --git a/src/components/Files/Files.tsx b/src/components/Files/Files.tsx index 577dfc1..c630db3 100644 --- a/src/components/Files/Files.tsx +++ b/src/components/Files/Files.tsx @@ -77,7 +77,7 @@ const Files: React.FC<{ updateSelectedFile: Function; updateBillType: Function; }> = (props) => { - const { selectedFile, updateSelectedFile, activeTempId, updateActiveTempId } = + const { selectedFile, updateSelectedFile, activeTemplateData, updateActiveTemplateData } = useInvoice(); const { isDarkMode } = useTheme(); const history = useHistory(); @@ -107,6 +107,9 @@ const Files: React.FC<{ number | "all" >("all"); + // Screen size state + const [isSmallScreen, setIsSmallScreen] = useState(false); + // Blockchain state removed - local-only mode // Template helper functions @@ -122,21 +125,34 @@ const Files: React.FC<{ const getFileTemplateInfo = (fileData: any) => { return fileData.templateMetadata || null; }; - - const handleSaveUnsavedChanges = async () => { - // No longer need to handle default file changes - // since we removed the default file concept - }; // Edit local file const editFile = async (key: string) => { try { - console.log("Attempting to edit file:", key); + console.log("Opening file:", key); - await handleSaveUnsavedChanges(); - // Simply navigate to the editor - let Home.tsx handle file loading and SocialCalc initialization - history.push(`/app/editor/${encodeURIComponent(key)}`); + // Clear any existing context state to prevent conflicts with old state + const fileData = await props.store._getFile(key); + const templateId = fileData?.templateId || 1; + const templateData = DATA[templateId]; + if (!templateData) { + setToastMessage("Template not found"); + setShowToast(true); + return; + } + updateSelectedFile(key); + updateActiveTemplateData(templateData); + + + // Optional: Show loading feedback + setToastMessage(`Opening ${key}...`); + setShowToast(true); + // Small delay to ensure context is cleared before navigation + setTimeout(() => { + history.push(`/app/editor/${encodeURIComponent(key)}`); + }, 10); + } catch (error) { console.error("Error in editFile:", error); setToastMessage("Failed to navigate to editor"); @@ -508,26 +524,19 @@ const Files: React.FC<{ className="file-icon document-icon" /> -

{file.name}

+
+

{file.name}

+ {file.templateMetadata && ( + + + {file.templateMetadata.template} + + )} +

Local file • {getLocalFileDateInfo(file).label}:{" "} {_formatDate(getLocalFileDateInfo(file).value)}

- {file.templateMetadata && ( -
- - - {file.templateMetadata.template} - - {file.templateMetadata.footers.length > 0 && ( - - - {file.templateMetadata.footers.length} footer(s) - - - )} -
- )}
-

{file.name}

+
+

{file.name}

+ {file.templateMetadata && ( + + + {file.templateMetadata.template} + + )} +

Local file • {getLocalFileDateInfo(file).label}:{" "} {_formatDate(getLocalFileDateInfo(file).value)}

- {file.templateMetadata && ( -
- - - {file.templateMetadata.template} - - {file.templateMetadata.footers.length > 0 && ( - - - {file.templateMetadata.footers.length} footer(s) - - - )} -
- )}
{ + const checkScreenSize = () => { + setIsSmallScreen(window.innerWidth < 692); + }; + + checkScreenSize(); + window.addEventListener('resize', checkScreenSize); + return () => window.removeEventListener('resize', checkScreenSize); + }, []); + // Reset sort option when switching file sources to ensure compatibility useEffect(() => { if (sortBy === "date") { @@ -727,32 +740,56 @@ const Files: React.FC<{ display: "flex", alignItems: "center", flex: "1", - minWidth: "140px", - maxWidth: "180px", + minWidth: isSmallScreen ? "32px" : "140px", + maxWidth: isSmallScreen ? "32px" : "180px", }} > - setSelectedTemplateFilter(e.detail.value)} - style={{ - flex: "1", - "--placeholder-color": "var(--ion-color-medium)", - "--color": "var(--ion-color-dark)", - }} - interface="popover" - > - All Templates - {getAvailableTemplates().map((template) => ( - - {template.template} - - ))} - + {!isSmallScreen && ( + setSelectedTemplateFilter(e.detail.value)} + style={{ + flex: "1", + "--placeholder-color": "var(--ion-color-medium)", + "--color": "var(--ion-color-dark)", + }} + interface="popover" + > + All Templates + {getAvailableTemplates().map((template) => ( + + {template.template} + + ))} + + )} + {isSmallScreen && ( + setSelectedTemplateFilter(e.detail.value)} + style={{ + flex: "1", + "--placeholder-color": "var(--ion-color-medium)", + "--color": "var(--ion-color-dark)", + width: "5px", + minWidth: "5px", + }} + interface="popover" + > + All Templates + {getAvailableTemplates().map((template) => ( + + {template.template} + + ))} + + )}
- setSortBy(e.detail.value)} - style={{ - flex: "1", - "--placeholder-color": "var(--ion-color-medium)", - "--color": "var(--ion-color-dark)", - }} - interface="popover" - > - {fileSource === "local" ? ( - <> - - By Date Modified - - - By Date Created - - By Name - - ) : ( - <> - By Date - By Name - - )} - + {!isSmallScreen && ( + setSortBy(e.detail.value)} + style={{ + flex: "1", + "--placeholder-color": "var(--ion-color-medium)", + "--color": "var(--ion-color-dark)", + }} + interface="popover" + > + {fileSource === "local" ? ( + <> + + By Date Modified + + + By Date Created + + By Name + + ) : ( + <> + By Date + By Name + + )} + + )} + {isSmallScreen && ( + setSortBy(e.detail.value)} + style={{ + flex: "1", + "--placeholder-color": "var(--ion-color-medium)", + "--color": "var(--ion-color-dark)", + width: "5px", + minWidth: "5px", + }} + interface="popover" + > + {fileSource === "local" ? ( + <> + + By Date Modified + + + By Date Created + + By Name + + ) : ( + <> + By Date + By Name + + )} + + )}
@@ -833,47 +904,50 @@ const Files: React.FC<{ ]} /> - { - setShowRenameAlert(false); - setCurrentRenameKey(null); - setRenameFileName(""); - }} - header="Rename File" - message={`Enter a new name for "${currentRenameKey}"`} - inputs={[ - { - name: "filename", - type: "text", - value: renameFileName, - placeholder: "Enter new filename", - }, - ]} - buttons={[ - { - text: "Cancel", - role: "cancel", - handler: () => { - setCurrentRenameKey(null); - setRenameFileName(""); + {/* Rename File Alert Wrapper */} + {showRenameAlert && currentRenameKey && ( + { + setShowRenameAlert(false); + setCurrentRenameKey(null); + setRenameFileName(""); + }} + header="Rename File" + message={`Enter a new name for "${currentRenameKey}"`} + inputs={[ + { + name: "filename", + type: "text", + value: renameFileName, + placeholder: "Enter new filename", }, - }, - { - text: "Rename", - handler: (data) => { - const newFileName = data.filename?.trim(); - if (newFileName) { - handleRename(newFileName); - } else { - setToastMessage("Filename cannot be empty"); - setShowToast(true); - } + ]} + buttons={[ + { + text: "Cancel", + role: "cancel", + handler: () => { + setCurrentRenameKey(null); + setRenameFileName(""); + }, }, - }, - ]} - /> + { + text: "Rename", + handler: (data) => { + const newFileName = data.filename?.trim(); + if (newFileName) { + handleRename(newFileName); + } else { + setToastMessage("Filename cannot be empty"); + setShowToast(true); + } + }, + }, + ]} + /> + )} setShowToast(false)} diff --git a/src/components/Storage/LocalStorage.ts b/src/components/Storage/LocalStorage.ts index a9a0659..84f4b7b 100644 --- a/src/components/Storage/LocalStorage.ts +++ b/src/components/Storage/LocalStorage.ts @@ -1,32 +1,7 @@ import { Preferences } from "@capacitor/preferences"; import CryptoJS from "crypto-js"; -// Enhanced Template Metadata Interface -export interface TemplateMetadata { - template: string; - templateId: number; - footers: { - name: string; - index: number; - isActive: boolean; - }[]; - logoCell: string | { [footerIndex: number]: string }; - signatureCell: string | { [footerIndex: number]: string }; - cellMappings: { - [footerIndex: number]: { - [fieldName: string]: - | string - | { [subField: string]: any } - | { - name?: string; - Range?: { start: number; end: number }; - Content?: { [fieldName: string]: string }; - }; - }; - }; -} - -// Enhanced File class with template metadata +// Enhanced File class with template ID only export class File { created: string; modified: string; @@ -35,7 +10,7 @@ export class File { billType: number; isEncrypted: boolean; password?: string; - templateMetadata: TemplateMetadata; + templateId: number; constructor( created: string, @@ -43,7 +18,7 @@ export class File { content: string, name: string, billType: number, - templateMetadataOrIsEncrypted?: TemplateMetadata | boolean, + templateIdOrIsEncrypted?: number | boolean, isEncryptedOrPassword?: boolean | string, password?: string ) { @@ -54,30 +29,14 @@ export class File { this.billType = billType; // Handle backward compatibility - if (typeof templateMetadataOrIsEncrypted === "boolean") { + if (typeof templateIdOrIsEncrypted === "boolean") { // Old constructor signature: (created, modified, content, name, billType, isEncrypted, password) - this.isEncrypted = templateMetadataOrIsEncrypted; + this.isEncrypted = templateIdOrIsEncrypted; this.password = isEncryptedOrPassword as string; - - // Create default template metadata - this.templateMetadata = { - template: `Template ${billType}`, - templateId: billType, - footers: [], - logoCell: null, - signatureCell: null, - cellMappings: {}, - }; + this.templateId = billType; // Use billType as default template ID for backward compatibility } else { - // New constructor signature: (created, modified, content, name, billType, templateMetadata, isEncrypted, password) - this.templateMetadata = templateMetadataOrIsEncrypted || { - template: `Template ${billType}`, - templateId: billType, - footers: [], - logoCell: null, - signatureCell: null, - cellMappings: {}, - }; + // New constructor signature: (created, modified, content, name, billType, templateId, isEncrypted, password) + this.templateId = templateIdOrIsEncrypted || billType; this.isEncrypted = (isEncryptedOrPassword as boolean) || false; this.password = password; } @@ -107,7 +66,7 @@ export class Local { name: file.name, billType: file.billType, isEncrypted: file.isEncrypted, - templateMetadata: file.templateMetadata, + templateId: file.templateId, }; // If file is password protected, encrypt the content @@ -155,7 +114,7 @@ export class Local { created: (data as any).created, modified: (data as any).modified, isEncrypted: (data as any).isEncrypted || false, - templateMetadata: (data as any).templateMetadata || null, + templateId: (data as any).templateId || null, }; } return arr; @@ -190,7 +149,7 @@ export class Local { const templateFiles = {}; for (const [fileName, fileInfo] of Object.entries(allFiles)) { - if ((fileInfo as any).templateMetadata?.templateId === templateId) { + if ((fileInfo as any).templateId === templateId) { templateFiles[fileName] = fileInfo; } } @@ -198,26 +157,21 @@ export class Local { return templateFiles; }; - // Get template metadata for a specific file - _getTemplateMetadata = async ( - fileName: string - ): Promise => { + // Get template ID for a specific file + _getTemplateId = async (fileName: string): Promise => { try { const data = await this._getFile(fileName); - return data.templateMetadata || null; + return data.templateId || null; } catch (error) { return null; } }; - // Update template metadata for a file - _updateTemplateMetadata = async ( - fileName: string, - metadata: TemplateMetadata - ) => { + // Update template ID for a file + _updateTemplateId = async (fileName: string, templateId: number) => { try { const data = await this._getFile(fileName); - data.templateMetadata = metadata; + data.templateId = templateId; data.modified = new Date().toISOString(); await Preferences.set({ @@ -227,7 +181,7 @@ export class Local { return true; } catch (error) { - console.error("Error updating template metadata:", error); + console.error("Error updating template ID:", error); return false; } }; @@ -238,7 +192,7 @@ export class Local { const templateIds = new Set(); for (const fileInfo of Object.values(allFiles)) { - const templateId = (fileInfo as any).templateMetadata?.templateId; + const templateId = (fileInfo as any).templateId; if (templateId) { templateIds.add(templateId); } diff --git a/src/components/TemplateFiles.tsx b/src/components/TemplateFiles.tsx index 6424870..06bcc2c 100644 --- a/src/components/TemplateFiles.tsx +++ b/src/components/TemplateFiles.tsx @@ -16,7 +16,7 @@ import { IonToast, } from '@ionic/react'; import { documentText, folder, download, create, trash } from 'ionicons/icons'; -import { Local, File, TemplateMetadata } from './Storage/LocalStorage'; +import { Local, File } from './Storage/LocalStorage'; import { TemplateManager } from '../utils/templateManager'; import { TemplateInitializer } from '../utils/templateInitializer'; @@ -74,14 +74,14 @@ const TemplateFiles: React.FC = ({ onFileSelect, onFileCreat }; const getTemplateInfo = (templateId: number) => { - const metadata = TemplateInitializer.getTemplateMetadata(templateId); - return metadata ? metadata.template : `Template ${templateId}`; + const templateData = TemplateInitializer.getTemplateData(templateId); + return templateData ? templateData.template : `Template ${templateId}`; }; const handleFileCreate = async (templateId: number) => { try { - const metadata = TemplateInitializer.getTemplateMetadata(templateId); - if (!metadata) { + const templateData = TemplateInitializer.getTemplateData(templateId); + if (!templateData) { setToastMessage('Template not found'); return; } @@ -99,13 +99,13 @@ const TemplateFiles: React.FC = ({ onFileSelect, onFileCreat mscContent, fileName, templateId, - metadata, + templateId, false ); await local._saveFile(newFile); await loadFiles(); - setToastMessage(`File created with ${metadata.template}`); + setToastMessage(`File created with ${templateData.template}`); if (onFileCreate) { onFileCreate(templateId); @@ -127,8 +127,8 @@ const TemplateFiles: React.FC = ({ onFileSelect, onFileCreat } }; - const getFileTemplateInfo = (fileData: any): TemplateMetadata | null => { - return fileData.templateMetadata || null; + const getFileTemplateInfo = (fileData: any): number | null => { + return fileData.templateId || null; }; const filteredFiles = getFilteredFiles(); @@ -186,7 +186,8 @@ const TemplateFiles: React.FC = ({ onFileSelect, onFileCreat ) : ( {Object.entries(filteredFiles).map(([fileName, fileData]) => { - const templateMetadata = getFileTemplateInfo(fileData); + const templateId = getFileTemplateInfo(fileData); + const templateData = templateId ? TemplateInitializer.getTemplateData(templateId) : null; return ( @@ -198,14 +199,14 @@ const TemplateFiles: React.FC = ({ onFileSelect, onFileCreat <> • Modified: {new Date(fileData.modified).toLocaleDateString()} )}
- {templateMetadata && ( + {templateData && (
- {templateMetadata.template} + {templateData.template} - {templateMetadata.footers.length > 0 && ( + {templateData.footers.length > 0 && ( - {templateMetadata.footers.length} footer(s) + {templateData.footers.length} footer(s) )} {fileData.isEncrypted && ( @@ -218,7 +219,7 @@ const TemplateFiles: React.FC = ({ onFileSelect, onFileCreat
onFileSelect && onFileSelect(fileName, templateMetadata?.templateId || 1)} + onClick={() => onFileSelect && onFileSelect(fileName, templateId || 1)} > diff --git a/src/contexts/InvoiceContext.tsx b/src/contexts/InvoiceContext.tsx index 0508fa4..cfc2495 100644 --- a/src/contexts/InvoiceContext.tsx +++ b/src/contexts/InvoiceContext.tsx @@ -6,15 +6,16 @@ import React, { ReactNode, } from "react"; import { Local } from "../components/Storage/LocalStorage"; +import { TemplateData, DATA } from "../templates"; interface InvoiceContextType { selectedFile: string; billType: number; store: Local; - activeTempId: number; + activeTemplateData: TemplateData | null; updateSelectedFile: (fileName: string) => void; updateBillType: (type: number) => void; - updateActiveTempId: (tempId: number) => void; + updateActiveTemplateData: (templateData: TemplateData | null) => void; resetToDefaults: () => void; } @@ -37,7 +38,7 @@ export const InvoiceProvider: React.FC = ({ }) => { const [selectedFile, setSelectedFile] = useState("default"); const [billType, setBillType] = useState(1); - const [activeTempId, setActiveTempId] = useState(1); + const [activeTemplateData, setActiveTemplateData] = useState(null); const [store] = useState(() => new Local()); // Load persisted state from localStorage on mount @@ -45,7 +46,7 @@ export const InvoiceProvider: React.FC = ({ try { const savedFile = localStorage.getItem("stark-invoice-selected-file"); const savedBillType = localStorage.getItem("stark-invoice-bill-type"); - const savedActiveTempId = localStorage.getItem("stark-invoice-active-temp-id"); + const savedActiveTemplateId = localStorage.getItem("stark-invoice-active-template-id"); if (savedFile) { setSelectedFile(savedFile); @@ -55,8 +56,12 @@ export const InvoiceProvider: React.FC = ({ setBillType(parseInt(savedBillType, 10)); } - if (savedActiveTempId) { - setActiveTempId(parseInt(savedActiveTempId, 10)); + if (savedActiveTemplateId) { + const templateId = parseInt(savedActiveTemplateId, 10); + const templateData = DATA[templateId]; + if (templateData) { + setActiveTemplateData(templateData); + } } } catch (error) { console.warn("Failed to load invoice state from localStorage:", error); @@ -82,11 +87,15 @@ export const InvoiceProvider: React.FC = ({ useEffect(() => { try { - localStorage.setItem("stark-invoice-active-temp-id", activeTempId.toString()); + if (activeTemplateData) { + localStorage.setItem("stark-invoice-active-template-id", activeTemplateData.templateId.toString()); + } else { + localStorage.removeItem("stark-invoice-active-template-id"); + } } catch (error) { - console.warn("Failed to save active temp id to localStorage:", error); + console.warn("Failed to save active template id to localStorage:", error); } - }, [activeTempId]); + }, [activeTemplateData]); const updateSelectedFile = (fileName: string) => { setSelectedFile(fileName); @@ -96,24 +105,24 @@ export const InvoiceProvider: React.FC = ({ setBillType(type); }; - const updateActiveTempId = (tempId: number) => { - setActiveTempId(tempId); + const updateActiveTemplateData = (templateData: TemplateData | null) => { + setActiveTemplateData(templateData); }; const resetToDefaults = () => { setSelectedFile("default"); setBillType(1); - setActiveTempId(1); + setActiveTemplateData(null); }; const value: InvoiceContextType = { selectedFile, billType, store, - activeTempId, + activeTemplateData, updateSelectedFile, updateBillType, - updateActiveTempId, + updateActiveTemplateData, resetToDefaults, }; diff --git a/src/pages/FilesPage.css b/src/pages/FilesPage.css index 439cb5a..27fbc87 100644 --- a/src/pages/FilesPage.css +++ b/src/pages/FilesPage.css @@ -8,6 +8,30 @@ display: none; /* Hide the original icon since it's now a full page */ } +/* Hide scrollbar for mobile template preview */ +.template-preview-scroll { + scrollbar-width: thin; /* Firefox - show thin scrollbar */ + scrollbar-color: var(--ion-color-medium-tint) transparent; /* Firefox scrollbar colors */ +} + +.template-preview-scroll::-webkit-scrollbar { + height: 6px; /* WebKit - show thin scrollbar */ +} + +.template-preview-scroll::-webkit-scrollbar-track { + background: var(--ion-color-step-100); + border-radius: 3px; +} + +.template-preview-scroll::-webkit-scrollbar-thumb { + background: var(--ion-color-medium-tint); + border-radius: 3px; +} + +.template-preview-scroll::-webkit-scrollbar-thumb:hover { + background: var(--ion-color-medium); +} + /* Enhanced mobile file list styling */ @media (max-width: 768px) { .files-page-container { diff --git a/src/pages/FilesPage.tsx b/src/pages/FilesPage.tsx index 60a1a26..b22e7cf 100644 --- a/src/pages/FilesPage.tsx +++ b/src/pages/FilesPage.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { IonAlert, IonContent, @@ -10,13 +10,26 @@ import { IonButton, IonIcon, IonButtons, + IonModal, + IonFab, + IonFabButton, + IonSegment, + IonSegmentButton, + IonLabel, + IonText, } from "@ionic/react"; import { chevronForward, - chevronUp, - chevronDown, layers, settings, + add, + close, + phonePortraitOutline, + tabletPortraitOutline, + desktopOutline, + filterOutline, + moon, + sunny, } from "ionicons/icons"; import Files from "../components/Files/Files"; import { useTheme } from "../contexts/ThemeContext"; @@ -30,7 +43,7 @@ import { File } from "../components/Storage/LocalStorage"; import { TemplateInitializer } from "../utils/templateInitializer"; const FilesPage: React.FC = () => { - const { isDarkMode } = useTheme(); + const { isDarkMode, toggleDarkMode } = useTheme(); const { selectedFile, billType, @@ -42,37 +55,141 @@ const FilesPage: React.FC = () => { const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); - const [showAllTemplates, setShowAllTemplates] = useState(false); const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); const [selectedTemplateForFile, setSelectedTemplateForFile] = useState(null); const [newFileName, setNewFileName] = useState(""); + const [showTemplateModal, setShowTemplateModal] = useState(false); + const [isSmallScreen, setIsSmallScreen] = useState(false); + const [templateFilter, setTemplateFilter] = useState<"all" | "web" | "mobile" | "tablet">("all"); const [device] = useState(AppGeneral.getDeviceType()); + // Check screen size + useEffect(() => { + const checkScreenSize = () => { + setIsSmallScreen(window.innerWidth < 692); + }; + + checkScreenSize(); + window.addEventListener('resize', checkScreenSize); + return () => window.removeEventListener('resize', checkScreenSize); + }, []); + + // Clear selected file when navigating to files page to prevent conflicts + useEffect(() => { + // Clear the selected file to prevent infinite loops when navigating back + if (selectedFile && selectedFile !== "") { + console.log("Clearing selected file when navigating to files page"); + updateSelectedFile(""); + } + }, []); + // Template helper functions const getAvailableTemplates = () => { return TemplateInitializer.getAllTemplates(); }; - const getTemplateInfo = (templateId: number) => { - const template = TemplateInitializer.getTemplate(templateId); - return template ? template.template : `Template ${templateId}`; - }; - const getTemplateMetadata = (templateId: number) => { return tempMeta.find(meta => meta.template_id === templateId); }; + // Categorize templates based on their names + const categorizeTemplate = (templateName: string) => { + const name = templateName.toLowerCase(); + if (name.includes('mobile')) { + return 'mobile'; + } else if (name.includes('tablet')) { + return 'tablet'; + } else { + return 'web'; + } + }; + + // Get categorized templates + const getCategorizedTemplates = () => { + const templates = getAvailableTemplates(); + const categorized = { + web: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.templateId)?.name || t.template) === 'web'), + mobile: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.templateId)?.name || t.template) === 'mobile'), + tablet: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.templateId)?.name || t.template) === 'tablet'), + }; + return categorized; + }; + + // Get filtered templates based on current filter + const getFilteredTemplates = () => { + const categorized = getCategorizedTemplates(); + + if (templateFilter === 'all') { + // Return in order: web, mobile, tablet + return [...categorized.web, ...categorized.mobile, ...categorized.tablet]; + } else { + return categorized[templateFilter] || []; + } + }; + const handleTemplateSelect = (templateId: number) => { setSelectedTemplateForFile(templateId); setShowFileNamePrompt(true); + if (isSmallScreen) { + setShowTemplateModal(false); + } + }; + + // Reset template filter when modal closes + const handleModalClose = () => { + setShowTemplateModal(false); + setTemplateFilter("all"); + }; + + /* Utility functions */ + const _validateName = async (filename: string) => { + filename = filename.trim(); + if (filename === "Untitled") { + return { + isValid: false, + message: "cannot update Untitled file! Use Save As Button to save." + }; + } else if (filename === "" || !filename) { + return { + isValid: false, + message: "Filename cannot be empty" + }; + } else if (filename.length > 30) { + return { + isValid: false, + message: "Filename too long" + }; + } else if (/^[a-zA-Z0-9- ]*$/.test(filename) === false) { + return { + isValid: false, + message: "Special Characters cannot be used" + }; + } else if (await store._checkKey(filename)) { + return { + isValid: false, + message: "Filename already exists" + }; + } + return { + isValid: true, + message: "" + }; }; // Create new file with template const createNewFileWithTemplate = async (templateId: number, fileName: string) => { try { - const metadata = TemplateInitializer.getTemplateMetadata(templateId); - if (!metadata) { + // Validate filename first + const validation = await _validateName(fileName); + if (!validation.isValid) { + setToastMessage(validation.message); + setShowToast(true); + return; + } + + const templateData = TemplateInitializer.getTemplateData(templateId); + if (!templateData) { setToastMessage("Template not found"); setShowToast(true); return; @@ -85,35 +202,35 @@ const FilesPage: React.FC = () => { return; } + // Find the active footer index, default to 1 if none found + const activeFooter = templateData.footers?.find(footer => footer.isActive); + const activeFooterIndex = activeFooter ? activeFooter.index : 1; + const now = new Date().toISOString(); const newFile = new File( now, now, encodeURIComponent(mscContent), // mscContent is already a JSON string fileName, + activeFooterIndex, templateId, - metadata, false ); await store._saveFile(newFile); - setToastMessage(`File "${fileName}" created with ${metadata.template}`); + setToastMessage(`File "${fileName}" created with ${templateData.template}`); setShowToast(true); // Reset modal state setShowFileNamePrompt(false); setSelectedTemplateForFile(null); setNewFileName(""); + setShowTemplateModal(false); // Dismiss the template modal - // Navigate to editor with the new file using the new URL structure updateSelectedFile(fileName); - updateBillType(templateId); - - // Don't initialize SocialCalc here - let the Home page handle initialization - // when it loads with the selected file - - history.push(`/app/editor/${encodeURIComponent(fileName)}`); + updateBillType(1); + history.replace(`/app/editor/${encodeURIComponent(fileName)}`); } catch (error) { console.error("Error creating file:", error); setToastMessage("Failed to create file"); @@ -121,19 +238,388 @@ const FilesPage: React.FC = () => { } }; - const handleNewFileClick = async () => { - // Directly show the template selection modal - // No need to check for unsaved changes since we removed the default file - setShowAllTemplates(false); - }; - const handleNewMedClick = async () => { - // Directly show the template selection modal - // No need to check for unsaved changes since we removed the default file - setShowAllTemplates(false); + // Render template modal for small screens + const renderTemplateModal = () => { + const filteredTemplates = getFilteredTemplates(); + const categorized = getCategorizedTemplates(); + + return ( + + + + Choose Template + + + + + + + + + {/* Filter Segment */} +
+ setTemplateFilter(e.detail.value as "all" | "web" | "mobile" | "tablet")} + style={{ + background: isDarkMode ? "var(--ion-color-step-150)" : "var(--ion-background-color)", + borderRadius: "8px", + padding: "3px", + border: `1px solid ${isDarkMode ? "var(--ion-color-step-250)" : "var(--ion-color-step-150)"}`, + boxShadow: "none", + '--background': isDarkMode ? 'var(--ion-color-step-150)' : 'var(--ion-background-color)', + '--background-checked': isDarkMode ? 'var(--ion-color-primary)' : 'var(--ion-color-primary)', + '--color': isDarkMode ? '#ffffff' : '#000000', + '--color-checked': '#ffffff' + }} + > + + + + All ({categorized.web.length + categorized.mobile.length + categorized.tablet.length}) + + + + + + Web ({categorized.web.length}) + + + + + + Mobile ({categorized.mobile.length}) + + + + + + Tablet ({categorized.tablet.length}) + + + +
+ +
+ {filteredTemplates.length === 0 ? ( +
+ +

+ No Templates Found +

+

+ No templates found for {templateFilter === "all" ? "this filter" : templateFilter} category +

+
+ ) : ( + <> + {templateFilter === "all" && ( + <> + {/* Web Templates Section */} + {categorized.web.length > 0 && ( + <> +
+ + Web Templates ({categorized.web.length}) +
+ {categorized.web.map((template) => renderTemplateItem(template))} + {(categorized.mobile.length > 0 || categorized.tablet.length > 0) && ( +
+ )} + + )} + + {/* Mobile Templates Section */} + {categorized.mobile.length > 0 && ( + <> +
+ + Mobile Templates ({categorized.mobile.length}) +
+ {categorized.mobile.map((template) => renderTemplateItem(template))} + {categorized.tablet.length > 0 && ( +
+ )} + + )} + + {/* Tablet Templates Section */} + {categorized.tablet.length > 0 && ( + <> +
+ + Tablet Templates ({categorized.tablet.length}) +
+ {categorized.tablet.map((template) => renderTemplateItem(template))} + + )} + + )} + + {templateFilter !== "all" && ( + filteredTemplates.map((template) => renderTemplateItem(template)) + )} + + )} +
+ + + ); }; - // Removed createNewFile and createNewMed functions since they handled default file logic - // Now we only use createNewFileWithTemplate for creating files with specific templates + // Helper function to render individual template items + const renderTemplateItem = (template: any) => { + const metadata = getTemplateMetadata(template.templateId); + const category = categorizeTemplate(metadata?.name || template.template); + + return ( +
handleTemplateSelect(template.templateId)} + style={{ + border: `1px solid ${isDarkMode ? "var(--ion-color-step-200)" : "var(--ion-color-step-150)"}`, + borderRadius: "8px", + padding: "12px", + marginBottom: "12px", + cursor: "pointer", + backgroundColor: isDarkMode ? "var(--ion-color-step-50)" : "var(--ion-background-color)", + display: "flex", + alignItems: "center", + gap: "12px", + transition: "all 0.2s ease" + }} + onMouseOver={(e) => { + e.currentTarget.style.backgroundColor = isDarkMode ? "var(--ion-color-step-100)" : "var(--ion-color-step-50)"; + e.currentTarget.style.borderColor = isDarkMode ? "var(--ion-color-step-300)" : "var(--ion-color-step-200)"; + }} + onMouseOut={(e) => { + e.currentTarget.style.backgroundColor = isDarkMode ? "var(--ion-color-step-50)" : "var(--ion-background-color)"; + e.currentTarget.style.borderColor = isDarkMode ? "var(--ion-color-step-200)" : "var(--ion-color-step-150)"; + }} + > + {/* Template Image */} +
+ {metadata?.ImageUri ? ( + {metadata.name} + ) : ( + + )} +
+ + {/* Template Info */} +
+

+ {metadata?.name || template.template} +

+

+ {template.footers.length} footer{template.footers.length !== 1 ? 's' : ''} +

+ {/* Category Badge */} +
+ {category} +
+
+ + {/* Arrow Icon */} + +
+ ); + }; return ( @@ -142,14 +628,29 @@ const FilesPage: React.FC = () => { - 🧾 Invoice App + Invoice App + {" "}Invoice App + + + history.push("/app/settings")} @@ -161,146 +662,364 @@ const FilesPage: React.FC = () => { - {/* Template Creation Section with Template Cards */} -
-

+
- Create New File -

- - {/* Template Cards - Show first 3, then expand button if more */} -
- {getAvailableTemplates() - .slice(0, showAllTemplates ? undefined : 3) - .map((template) => { - const metadata = getTemplateMetadata(template.templateId); - return ( -
handleTemplateSelect(template.templateId)} - style={{ - border: "2px solid var(--ion-color-light)", - borderRadius: "12px", - padding: "20px", - cursor: "pointer", - transition: "all 0.3s ease", - backgroundColor: "var(--ion-color-light-tint)", - display: "flex", - alignItems: "center", - gap: "16px", - boxShadow: "0 2px 8px rgba(0,0,0,0.1)" - }} - onMouseOver={(e) => { - e.currentTarget.style.borderColor = "var(--ion-color-primary)"; - e.currentTarget.style.transform = "translateY(-4px)"; - e.currentTarget.style.boxShadow = "0 8px 24px rgba(0,0,0,0.15)"; - }} - onMouseOut={(e) => { - e.currentTarget.style.borderColor = "var(--ion-color-light)"; - e.currentTarget.style.transform = "translateY(0)"; - e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.1)"; - }} - > - {/* Template Image */} -
- {metadata?.ImageUri ? ( - {metadata.name} - ) : ( +

+ Create New File +

+
+ + {/* Desktop: Template Cards - Show only first 3 */} + {!isSmallScreen && ( + <> +
+ {getAvailableTemplates() + .slice(0, 3) + .map((template) => { + const metadata = getTemplateMetadata(template.templateId); + return ( +
handleTemplateSelect(template.templateId)} + style={{ + border: "2px solid var(--ion-color-light)", + borderRadius: "12px", + padding: "20px", + cursor: "pointer", + transition: "all 0.3s ease", + backgroundColor: "var(--ion-color-light-tint)", + display: "flex", + alignItems: "center", + gap: "16px", + boxShadow: "0 2px 8px rgba(0,0,0,0.1)" + }} + onMouseOver={(e) => { + e.currentTarget.style.borderColor = "var(--ion-color-primary)"; + e.currentTarget.style.transform = "translateY(-4px)"; + e.currentTarget.style.boxShadow = "0 8px 24px rgba(0,0,0,0.15)"; + }} + onMouseOut={(e) => { + e.currentTarget.style.borderColor = "var(--ion-color-light)"; + e.currentTarget.style.transform = "translateY(0)"; + e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.1)"; + }} + > + {/* Template Image */} +
+ {metadata?.ImageUri ? ( + {metadata.name} + ) : ( + + )} +
+ + {/* Template Info */} +
+

+ {metadata?.name || template.template} +

+

+ {template.footers.length} footer(s) +

+
+ + {/* Arrow Icon */} - )} -
- - {/* Template Info */} -
-

- {metadata?.name || template.template} -

-

- {template.footers.length} footer(s) -

-
- - {/* Arrow Icon */} +
+ ); + })} + + {/* Plus icon card to show more templates */} +
setShowTemplateModal(true)} + style={{ + border: "2px dashed var(--ion-color-light)", + borderRadius: "12px", + padding: "20px", + cursor: "pointer", + transition: "all 0.3s ease", + backgroundColor: "transparent", + display: "flex", + alignItems: "center", + justifyContent: "center", + gap: "16px", + boxShadow: "0 2px 8px rgba(0,0,0,0.05)" + }} + onMouseOver={(e) => { + e.currentTarget.style.borderColor = "var(--ion-color-primary)"; + e.currentTarget.style.transform = "translateY(-4px)"; + e.currentTarget.style.boxShadow = "0 8px 24px rgba(0,0,0,0.1)"; + }} + onMouseOut={(e) => { + e.currentTarget.style.borderColor = "var(--ion-color-light)"; + e.currentTarget.style.transform = "translateY(0)"; + e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.05)"; + }} + > + {/* Plus Icon */} +
- ); - })} -
+ + {/* More Info */} +
+

+ More Templates +

+

+ View all available templates +

+
+ + {/* Arrow Icon */} + +
+
+ + )} - {/* Show More Templates Button */} - {getAvailableTemplates().length > 3 && ( -
- setShowAllTemplates(!showAllTemplates)} - style={{ margin: "0 auto" }} + {/* Mobile: Show template previews */} + {isSmallScreen && ( +
+ {getAvailableTemplates() + .slice(0, 3) + .map((template) => { + const metadata = getTemplateMetadata(template.templateId); + return ( +
handleTemplateSelect(template.templateId)} + style={{ + minWidth: "110px", + width: "110px", + border: `1px solid ${isDarkMode ? "var(--ion-color-step-200)" : "var(--ion-color-step-150)"}`, + borderRadius: "8px", + padding: "12px", + cursor: "pointer", + backgroundColor: isDarkMode ? "var(--ion-color-step-50)" : "var(--ion-background-color)", + display: "flex", + flexDirection: "column", + alignItems: "center", + gap: "8px", + transition: "all 0.2s ease", + boxShadow: "0 1px 3px rgba(0,0,0,0.1)", + flexShrink: 0 // Prevent cards from shrinking + }} + > + {/* Template Image */} +
+ {metadata?.ImageUri ? ( + {metadata.name} + ) : ( + + )} +
+ + {/* Template Name */} +
+

+ {metadata?.name || template.template} +

+
+
+ ); + })} + + {/* Plus icon card to show more templates */} +
setShowTemplateModal(true)} + style={{ + minWidth: "110px", + width: "110px", + border: `2px dashed ${isDarkMode ? "var(--ion-color-step-300)" : "var(--ion-color-step-200)"}`, + borderRadius: "8px", + padding: "12px", + cursor: "pointer", + backgroundColor: "transparent", + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + gap: "8px", + transition: "all 0.2s ease", + flexShrink: 0 // Prevent card from shrinking + }} > - - {showAllTemplates ? 'Show Less' : `View ${getAvailableTemplates().length - 3} More Templates`} - +
+ +
+ +
+

+ More +

+
+
)}
+ + + {/* Template Modal for small screens */} + {renderTemplateModal()} { position="top" /> - {/* File Name Prompt Alert */} - { - setShowFileNamePrompt(false); - setSelectedTemplateForFile(null); - setNewFileName(""); - }} - header="Create New File" - message={ - selectedTemplateForFile && getTemplateMetadata(selectedTemplateForFile) - ? `Create a new ${getTemplateMetadata(selectedTemplateForFile)?.name} file` - : 'Create a new invoice file' - } - inputs={[ - { - name: "filename", - type: "text", - value: newFileName, - placeholder: "Enter file name", - }, - ]} - buttons={[ - { - text: "Cancel", - role: "cancel", - handler: () => { - setSelectedTemplateForFile(null); - setNewFileName(""); + {/* File Name Prompt Alert Wrapper */} + {showFileNamePrompt && selectedTemplateForFile !== null && getTemplateMetadata(selectedTemplateForFile) && ( + { + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + setNewFileName(""); + }} + header="Create New File" + message={`Create a new ${getTemplateMetadata(selectedTemplateForFile)?.name} file`} + inputs={[ + { + name: "filename", + type: "text", + value: newFileName, + placeholder: "Enter file name", }, - }, - { - text: "Create", - handler: (data) => { - const fileName = data.filename?.trim(); - if (fileName && selectedTemplateForFile) { - createNewFileWithTemplate(selectedTemplateForFile, fileName); - } else { - setToastMessage("Please enter a file name"); - setShowToast(true); - } + ]} + buttons={[ + { + text: "Cancel", + role: "cancel", + handler: () => { + setSelectedTemplateForFile(null); + console.log("File creation cancelled"); + setNewFileName(""); + }, }, - }, - ]} - /> + { + text: "Create", + handler: async (data) => { + const fileName = data.filename?.trim(); + if (!fileName) { + setToastMessage("Please enter a file name"); + setShowToast(true); + // Clear the filename and close the alert when validation fails + setNewFileName(""); + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + return false; // Prevent alert from closing automatically + } + + if (selectedTemplateForFile) { + // Validate the filename before creating + const validation = await _validateName(fileName); + if (!validation.isValid) { + setToastMessage(validation.message); + setShowToast(true); + // Clear the filename and close the alert when validation fails + setNewFileName(""); + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + return false; // Prevent alert from closing automatically + } + + // If validation passes, create the file + await createNewFileWithTemplate(selectedTemplateForFile, fileName); + return true; // Allow alert to close + } + return false; + }, + }, + ]} + /> + )} ); }; diff --git a/src/pages/Home.css b/src/pages/Home.css index d2c96f7..826807c 100644 --- a/src/pages/Home.css +++ b/src/pages/Home.css @@ -1,5 +1,5 @@ #container { - max-height: 70vh; + max-height: 100vh; /* position: relative; */ /* border: #eb24fd; */ /* border: 10px solid #eb24fd; */ @@ -23,7 +23,9 @@ max-height: 100vh; /* overflow: hidden; */ } - +#tableeditor{ + height: 100vh; +} /* Dark mode fixes for Home page */ .dark-theme ion-page { --ion-background-color: #0d1117 !important; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 7b78a19..063a3dc 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -22,6 +22,7 @@ import { IonSegmentButton, IonFab, IonFabButton, + IonSpinner, isPlatform, } from "@ionic/react"; import { APP_NAME, DATA } from "../templates"; @@ -70,16 +71,16 @@ import { TemplateManager } from "../utils/templateManager"; const Home: React.FC = () => { const { isDarkMode } = useTheme(); - const { selectedFile, billType, store, updateSelectedFile, updateBillType, activeTempId, updateActiveTempId } = + const { selectedFile, billType, store, updateSelectedFile, updateBillType, activeTemplateData, updateActiveTemplateData } = useInvoice(); - const { isInstallable, isInstalled, isOnline, installApp } = usePWA(); const history = useHistory(); const { fileName } = useParams<{ fileName?: string }>(); const [fileNotFound, setFileNotFound] = useState(false); + const [templateNotFound, setTemplateNotFound] = useState(false); + const [isInitializing, setIsInitializing] = useState(true); const [showMenu, setShowMenu] = useState(false); - const [device] = useState(AppGeneral.getDeviceType()); const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); const [toastColor, setToastColor] = useState< @@ -121,10 +122,6 @@ const Home: React.FC = () => { { name: "default", label: "Default", color: "#f4f5f8" }, ]; - const activateFooter = (footer) => { - AppGeneral.activateFooterButton(footer); - }; - const handleColorChange = (colorName: string) => { try { // Get the actual color value (hex) for the color name @@ -208,11 +205,28 @@ const Home: React.FC = () => { const performLocalSave = async (fileName: string) => { try { + // Check if SocialCalc is ready + const socialCalc = (window as any).SocialCalc; + if (!socialCalc || !socialCalc.GetCurrentWorkBookControl) { + setToastMessage("Spreadsheet not ready. Please wait and try again."); + setToastColor("warning"); + setShowToast(true); + return; + } + + const control = socialCalc.GetCurrentWorkBookControl(); + if (!control || !control.workbook || !control.workbook.spreadsheet) { + setToastMessage("Spreadsheet not ready. Please wait and try again."); + setToastColor("warning"); + setShowToast(true); + return; + } + const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); const now = new Date().toISOString(); - // Get template metadata for the current active template - const metadata = TemplateInitializer.getTemplateMetadata(activeTempId); + // Get template ID from active template data + const templateId = activeTemplateData ? activeTemplateData.templateId : billType; const file = new File( now, @@ -220,14 +234,7 @@ const Home: React.FC = () => { content, fileName, billType, - metadata || { - template: `Template ${activeTempId}`, - templateId: activeTempId, - footers: [], - logoCell: null, - signatureCell: null, - cellMappings: {}, - }, + templateId, false ); await store._saveFile(file); @@ -249,8 +256,31 @@ const Home: React.FC = () => { } }; - useEffect(() => { - const initializeApp = async () => { + const activateFooter = (footer) => { + // Only activate footer if SocialCalc is properly initialized + try { + const tableeditor = document.getElementById("tableeditor"); + const socialCalc = (window as any).SocialCalc; + + // Check if SocialCalc and WorkBook control are properly initialized + if (tableeditor && socialCalc && socialCalc.GetCurrentWorkBookControl) { + const control = socialCalc.GetCurrentWorkBookControl(); + if (control && control.workbook && control.workbook.spreadsheet) { + AppGeneral.activateFooterButton(footer); + } else { + console.log("SocialCalc WorkBook not ready for footer activation, skipping..."); + } + } else { + console.log("SocialCalc not ready for footer activation, skipping..."); + } + } catch (error) { + console.log("Error activating footer, SocialCalc might not be ready:", error); + } + }; + +const initializeApp = async () => { + setIsInitializing(true); + try { // Initialize template system first const isTemplateInitialized = await TemplateInitializer.isInitialized(); @@ -258,21 +288,28 @@ const Home: React.FC = () => { await TemplateInitializer.initializeApp(); } - // Determine which file to load - let fileToLoad = fileName || selectedFile; + // Prioritize URL parameter over context to ensure fresh state + let fileToLoad=selectedFile; + if (!selectedFile || selectedFile.trim() === "") { + fileToLoad = fileName; + updateSelectedFile(fileName); + } - // If no file is specified in URL or context, redirect to files page - if (!fileToLoad) { - console.log("No file specified, redirecting to files"); + // If no file is specified, redirect to files page + if (!fileToLoad || fileToLoad === "") { + // console.log("No file specified, redirecting to files"); + setIsInitializing(false); history.push("/app/files"); return; } // Check if the file exists in storage + console.log("file to load", fileToLoad); const fileExists = await store._checkKey(fileToLoad); if (!fileExists) { console.log(`File "${fileToLoad}" not found`); setFileNotFound(true); + setIsInitializing(false); return; } @@ -280,31 +317,84 @@ const Home: React.FC = () => { const fileData = await store._getFile(fileToLoad); const decodedContent = decodeURIComponent(fileData.content); - // Update context if URL parameter is different from selected file - if (fileName && fileName !== selectedFile) { - updateSelectedFile(fileName); + // Get template ID from file data + const templateId = fileData.templateId; + + // Check if template exists in the templates library + if (!DATA[templateId]) { + console.error(`Template ${templateId} not found in templates library`); + setTemplateNotFound(true); + setFileNotFound(false); + setIsInitializing(false); + return; } - // Use initializeApp instead of viewFile to ensure proper SocialCalc setup - AppGeneral.initializeApp(decodedContent); - updateBillType(fileData.billType); + // Load template data + const templateData = DATA[templateId]; + updateActiveTemplateData(templateData); + console.log(templateData); + console.log("Template data loaded successfully", fileData); + // Initialize SocialCalc with the file content + // console.log(`Initializing SocialCalc for file: ${fileToLoad}`); - // Update active template if file has template metadata - if (fileData.templateMetadata?.templateId) { - updateActiveTempId(fileData.templateMetadata.templateId); + // Wait a bit to ensure DOM elements are ready + setTimeout(() => { + try { + const currentControl = AppGeneral.getWorkbookInfo(); + console.log("Current workbook info:", currentControl); + + if (currentControl && currentControl.workbook) { + // SocialCalc is initialized, use viewFile + AppGeneral.viewFile(fileToLoad, decodedContent); + console.log("File loaded successfully with viewFile"); + } else { + // SocialCalc not initialized, initialize it first + console.log("SocialCalc not initialized, initializing..."); + AppGeneral.initializeApp(decodedContent); + console.log("File loaded successfully with initializeApp"); } - - console.log("Loaded file:", fileToLoad); + } catch (error) { + console.error("Error checking SocialCalc state:", error); + // Fallback: try to initialize the app + try { + AppGeneral.initializeApp(decodedContent); + console.log("File loaded successfully with initializeApp (fallback)"); + } catch (initError) { + console.error("initializeApp failed:", initError); + throw new Error( + "Failed to load file: SocialCalc initialization error" + ); + } + } + + // Activate footer after initialization + setTimeout(() => { + activateFooter(fileData.billType); + setIsInitializing(false); // Set loading to false after complete initialization + }, 500); + }, 100); + console.log("success"); + // console.log("Successfully loaded file:", fileToLoad); setFileNotFound(false); + setTemplateNotFound(false); } catch (error) { console.error("Error initializing app:", error); // On error, show file not found setFileNotFound(true); + setTemplateNotFound(false); + setIsInitializing(false); } - }; - +}; + + useEffect(() => { initializeApp(); - }, [fileName, selectedFile]); + }, [selectedFile]); // Only depend on selectedFile to prevent loops with selectedFile updates + + useEffect(() => { + if (fileName) { + updateSelectedFile(fileName); + } + }, [fileName]); const [autoSaveTimer, setAutoSaveTimer] = useState( null @@ -319,6 +409,19 @@ const Home: React.FC = () => { return; } + // Check if SocialCalc is ready + const socialCalc = (window as any).SocialCalc; + if (!socialCalc || !socialCalc.GetCurrentWorkBookControl) { + console.log("SocialCalc not ready for auto-save, skipping..."); + return; + } + + const control = socialCalc.GetCurrentWorkBookControl(); + if (!control || !control.workbook || !control.workbook.spreadsheet) { + console.log("SocialCalc WorkBook not ready for auto-save, skipping..."); + return; + } + const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); // Get existing metadata and update @@ -329,7 +432,7 @@ const Home: React.FC = () => { content, selectedFile, billType, - (data as any)?.templateMetadata, + activeTemplateData ? activeTemplateData.templateId : billType, false ); await store._saveFile(file); @@ -350,6 +453,7 @@ const Home: React.FC = () => { } } }; + useEffect(() => { const debouncedAutoSave = () => { if (autoSaveTimer) { @@ -363,9 +467,35 @@ const Home: React.FC = () => { setAutoSaveTimer(newTimer); }; - const removeListener = AppGeneral.setupCellChangeListener((_) => { - debouncedAutoSave(); - }); + let removeListener = () => {}; + + // Wait for SocialCalc to be ready before setting up the listener + const setupListener = () => { + try { + const socialCalc = (window as any).SocialCalc; + if (socialCalc && socialCalc.GetCurrentWorkBookControl) { + const control = socialCalc.GetCurrentWorkBookControl(); + if (control && control.workbook && control.workbook.spreadsheet) { + removeListener = AppGeneral.setupCellChangeListener((_) => { + debouncedAutoSave(); + }); + } else { + // Retry after a delay if WorkBook is not ready + setTimeout(setupListener, 2000); + } + } else { + // Retry after a delay if SocialCalc is not ready + setTimeout(setupListener, 2000); + } + } catch (error) { + console.log("Error setting up cell change listener:", error); + // Retry after a delay + setTimeout(setupListener, 2000); + } + }; + + // Start attempting to setup the listener + setupListener(); return () => { removeListener(); @@ -376,7 +506,12 @@ const Home: React.FC = () => { }, [selectedFile, billType, autoSaveTimer]); useEffect(() => { - activateFooter(billType); + // Add a delay to ensure SocialCalc is initialized before activating footer + const timer = setTimeout(() => { + activateFooter(billType); + }, 1000); + + return () => clearTimeout(timer); }, [billType]); // Effect to handle font color in dark mode @@ -414,7 +549,7 @@ const Home: React.FC = () => { } }, [isDarkMode, activeFontColor]); - const footers = DATA[activeTempId]["footers"]; + const footers = activeTemplateData ? activeTemplateData.footers : []; const footersList = footers.map((footerArray) => { const isActive = footerArray.index === billType; @@ -441,6 +576,12 @@ const Home: React.FC = () => { ); }); + useEffect(() => { + // Add a delay to ensure SocialCalc is initialized before activating footer + console.log("Selected file changed:", selectedFile); + console.log("activeTemplateData", activeTemplateData); + }, [selectedFile, activeTemplateData]); + return ( {
- - {isPlatform("mobile") || isPlatform("hybrid") ? ( - - {selectedFile.length > 15 - ? `${selectedFile.substring(0, 15)}...` - : selectedFile} - - ) : ( - {selectedFile} - )} + {selectedFile} {selectedFile && ( { /> - -
1 && ( + +
{ > {footersList}
-
- + + )} + - + {fileNotFound ? (
{ Go to File Explorer
+ ) : templateNotFound ? ( +
+ +

+ Template Not Found +

+

+ The file information is not downloaded. Please download the file template to open this file. +

+
+ history.push("/app/files")} + style={{ minWidth: "140px" }} + > + + Go to Files + + { + // Add download template functionality here + setToastMessage("Template download functionality coming soon"); + setToastColor("warning"); + setShowToast(true); + }} + style={{ minWidth: "140px" }} + > + + Download Template + +
+
) : ( -
-
-
-
+
+ {/* Loading overlay */} + {isInitializing && ( +
+ +

+ Initializing App +

+

+ Please wait while we load your invoice template and prepare the editor... +

+
+ )} + + {/* SocialCalc container - always rendered */} +
+
+
+
+
)} diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index d657956..eca850b 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -873,15 +873,6 @@ const SettingsPage: React.FC = () => { Settings - - - - - { - - - Dark Mode - toggleDarkMode()} - slot="end" - /> - diff --git a/src/template2.ts b/src/template2.ts deleted file mode 100644 index ae115c1..0000000 --- a/src/template2.ts +++ /dev/null @@ -1,522 +0,0 @@ -const a = { - 3: { - template: "Web Invoice 1", - templateId: 3, - msc: { - numsheets: 2, - currentid: "sheet1", - currentname: "typei", - sheetArr: { - sheet1: { - sheetstr: { - savestr: - "version:1.5\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE:b:1:1:1:1:f:6:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:t::b:2::2::l:1:f:7\ncell:G2:t::b:2::2::l:1:f:7\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2:::2:l:3:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:2::::l:1:f:7\ncell:G3:b:2:2:::l:3:f:7\ncell:A4:b::2:::l:3:f:7\ncell:B4:b::::2:l:3:f:3\ncell:C4:t:INVOICE # \\c:f:2:cf:2\ncell:D4:v:1:f:2:cf:2\ncell:F4:tvf:4:rowspan:4\ncell:G4:b::2:::l:3:f:7\ncell:A5:b::2:::l:3:f:7\ncell:B5:b::::2:l:3:f:4\ncell:F5:t::l:2:f:7\ncell:G5:t::b::1:::l:2:f:7\ncell:A6:b::2:::l:3:f:7\ncell:B6:b::::2:l:3:f:4\ncell:C6:t:INVOICE DATE\\c:f:2:cf:2\ncell:D6:f:2:cf:2:ntvf:3\ncell:F6:l:2:f:7\ncell:G6:b::1:::l:2:f:7\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::::2:l:3:f:4\ncell:G7:b::2:::l:3:f:7\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::::2:l:3:f:4\ncell:G8:b::2:::l:3:f:7\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::::2:l:3:f:4\ncell:C9:t:BILL TO\\c:l:3:f:2\ncell:E9:t:FROM\\c:f:2:cf:2\ncell:G9:b::2:::l:3:f:7\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::::2:l:3:f:3\ncell:C10:t:[Name]:f:1:cf:2:colspan:2\ncell:E10:t:[Name]:f:1:cf:2:colspan:2\ncell:G10:b::2:::l:3:f:7\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:E11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:G11:b::2:::l:3:f:7\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:E12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:G12:b::2:::l:3:f:7\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:Phone\\c:f:1:cf:2:colspan:2\ncell:E13:t:Phone\\c:f:1:cf:2:colspan:2\ncell:G13:b::2:::l:3:f:7\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::::2:l:3:f:3:cf:2\ncell:C14:b:::2::l:1:f:7\ncell:D14:b:::2::l:3:f:3:cf:2\ncell:E14:b:::2::l:1:f:7\ncell:F14:b:::2::l:1:f:7\ncell:G14:b::2:::l:3:f:7\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::2::2:l:3:f:7\ncell:C15:t:Description:b:1::1:1:f:2:cf:1:colspan:3\ncell:D15:t:Description:b:1::1::l:3:f:3:cf:2:colspan:2:rowspan:1\ncell:E15:t::b:2:2:2::l:1:f:7\ncell:F15:t:Amount:b:1:1:1:1:f:2:cf:1\ncell:G15:b::2::2:l:3:f:7\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::2::2:l:3:f:7\ncell:C16:b:1:1::1:f:1:cf:2:colspan:3\ncell:D16:b:1:1:::l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:E16:t::b:2:2:2::l:1:f:7\ncell:F16:b:1:1:::f:1:ntvf:1\ncell:G16:b::2::2:l:3:f:7\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::2::2:l:3:f:7:cf:2\ncell:C17:b::1::1:f:1:cf:2:colspan:3\ncell:D17:b::1:::l:1:f:7:colspan:2\ncell:E17:l:1:f:7\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::2::2:l:3:f:7\ncell:A18:b::2:::l:3:f:7\ncell:B18:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C18:b::1::1:f:1:cf:2:colspan:3\ncell:D18:b::1:::colspan:2\ncell:F18:b::1:::f:1:ntvf:1\ncell:G18:b::2::2:l:3:f:7\ncell:A19:b::2:::l:3:f:7\ncell:B19:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C19:b::1::1:f:1:cf:2:colspan:3\ncell:D19:b::1:::colspan:2\ncell:F19:b::1:::f:1:ntvf:1\ncell:G19:b::2::2:l:3:f:7\ncell:A20:b::2:::l:3:f:7\ncell:B20:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C20:b::1::1:f:1:cf:2:colspan:3\ncell:D20:b::1:::colspan:2\ncell:F20:b::1:::f:1:ntvf:1\ncell:G20:b::2::2:l:3:f:7\ncell:A21:b::2:::l:3:f:7\ncell:B21:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C21:b::1::1:f:1:cf:2:colspan:3\ncell:D21:b::1:::colspan:2\ncell:F21:b::1:::f:1:ntvf:1\ncell:G21:b::2::2:l:3:f:7\ncell:A22:b::2:::l:3:f:7\ncell:B22:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C22:b::1::1:f:1:cf:2:colspan:3\ncell:D22:b::1:::colspan:2\ncell:F22:b::1:::f:1:ntvf:1\ncell:G22:b::2::2:l:3:f:7\ncell:A23:b::2:::l:3:f:7\ncell:B23:b::2::2:l:3:f:7:cf:2\ncell:C23:b::1::1:f:1:cf:2:colspan:3\ncell:D23:b::1:::colspan:2\ncell:F23:b::1:::f:1:ntvf:1\ncell:G23:b::2::2:l:3:f:7\ncell:A24:b::2:::l:3:f:7\ncell:B24:b::2::2:l:3:f:7:cf:2\ncell:C24:b::1::1:f:1:cf:2:colspan:3\ncell:D24:b::1:::colspan:2\ncell:F24:b::1:::f:1:ntvf:1\ncell:G24:b::2::2:l:3:f:7\ncell:A25:b::2:::l:3:f:7\ncell:B25:b::2::2:l:3:f:7:cf:2\ncell:C25:b::1::1:f:1:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:1:ntvf:1\ncell:G25:b::2::2:l:3:f:7\ncell:A26:b::2:::l:3:f:7\ncell:B26:b::2::2:l:3:f:7:cf:2\ncell:C26:b::1::1:f:1:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:1:ntvf:1\ncell:G26:b::2::2:l:3:f:7\ncell:A27:b::2:::l:3:f:7\ncell:B27:b::2::2:l:3:f:7:cf:2\ncell:C27:b::1::1:f:1:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:1:ntvf:1\ncell:G27:b::2::2:l:3:f:7\ncell:A28:b::2:::l:3:f:4\ncell:B28:b::2::2:l:3:f:7:cf:2\ncell:C28:b::1:1:1:f:1:cf:2:colspan:3\ncell:D28:b::1:1::l:3:f:7:cf:2:colspan:2\ncell:E28:b:::2::l:3:f:7:cf:2\ncell:F28:b::1:1::f:1:ntvf:1\ncell:G28:b::2::2:l:3:f:7\ncell:A29:b::2:::l:3:f:3\ncell:B29:b::2::2:l:3:f:3\ncell:C29:t:TOTAL:b:1:1:1:1:f:5:cf:2:colspan:3\ncell:D29:b:2::2::l:3:f:8:cf:2\ncell:E29:b:2:2:2::l:3:f:8:cf:2\ncell:F29:vtf:n:0:SUM(F16\\cF28):b:1:1:1::f:5:ntvf:1\ncell:G29:b::2::2:l:3:f:4\ncell:A30:b::2:::l:3:f:7\ncell:B30:b::::2:l:3:f:3:cf:2\ncell:C30:b:2::::l:1:f:7\ncell:D30:b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:7\ncell:F30:b:2::::l:1:f:7\ncell:G30:b::2:::l:3:f:3\ncell:A31:b::2:::l:3:f:7\ncell:B31:b::::2:l:3:f:7\ncell:D31:tvf:4:rowspan:4\ncell:G31:b::2:::l:3:f:7\ncell:A32:b::2:::l:3:f:7\ncell:B32:b::::2:l:3:f:7\ncell:G32:b::2:::l:3:f:7\ncell:A33:b::2:::l:3:f:7\ncell:B33:b::::2:l:3:f:7\ncell:G33:b::2:::l:3:f:7\ncell:A34:b::2:::l:3:f:7\ncell:B34:b::::2:l:3:f:7\ncell:G34:b::2:::l:3:f:7\ncell:A35:b::2:::l:3:f:7\ncell:B35:b:::2:2:l:3:f:7\ncell:C35:b:::2::l:3:f:7\ncell:D35:b:::2::l:3:f:7\ncell:E35:b:::2::l:3:f:7\ncell:F35:b:::2::l:3:f:7\ncell:G35:b::2:2::l:3:f:7\ncell:B36:b:1:::\ncell:C36:b:1:::\ncell:D36:b:1:::\ncell:E36:b:1:::\ncell:F36:b:1:::\ncell:G36:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:105\ncol:D:w:182\ncol:E:w:110\ncol:F:w:115\ncol:G:w:65\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:15.75\nrow:29:h:15.75\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:7:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:normal bold * Trebuchet MS\nfont:3:normal bold 10pt Arial\nfont:4:normal bold 12pt Arial\nfont:5:normal bold 12pt Trebuchet MS\nfont:6:normal bold 14pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 12pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", - }, - name: "typei", - hidden: "0", - }, - sheet2: { - sheetstr: { - savestr: - 'version:1.5\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE:b:1:1:1:1:l:3:f:6:cf:1:colspan:8\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:t::b:2::2::l:1:f:7\ncell:G2:b:2::2::l:1:f:7\ncell:H2:b:2::2::l:1:f:7\ncell:I2:t::b:2::2::l:1:f:7\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2:::2:l:3:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:2::::l:1:f:7\ncell:G3:b:2::::l:1:f:7\ncell:H3:b:2::::l:1:f:7\ncell:I3:b:2:2:::l:3:f:7\ncell:A4:b::2:::l:3:f:7\ncell:B4:b::::2:l:3:f:3\ncell:C4:t:INVOICE # \\c:f:2:cf:2\ncell:D4:v:1:f:2:cf:2\ncell:F4:tvf:5:colspan:2:rowspan:4\ncell:I4:b::2:::l:3:f:7\ncell:A5:b::2:::l:3:f:7\ncell:B5:b::::2:l:3:f:4\ncell:F5:t::l:2:f:7\ncell:G5:l:2:f:7\ncell:H5:l:2:f:7\ncell:I5:t::b::1:::l:2:f:7\ncell:A6:b::2:::l:3:f:7\ncell:B6:b::::2:l:3:f:4\ncell:C6:t:INVOICE DATE\\c:f:2:cf:2\ncell:D6:f:2:cf:2:ntvf:4\ncell:F6:l:2:f:7\ncell:G6:l:2:f:7\ncell:H6:l:2:f:7\ncell:I6:b::1:::l:2:f:7\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::::2:l:3:f:4\ncell:I7:b::2:::l:3:f:7\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::::2:l:3:f:4\ncell:I8:b::2:::l:3:f:7\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::::2:l:3:f:4\ncell:C9:t:BILL TO\\c:f:2\ncell:E9:t:FROM\\c:f:2:cf:2\ncell:F9:colspan:3\ncell:I9:b::2:::l:3:f:7\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::::2:l:3:f:3\ncell:C10:t:[Name]:f:1:cf:2:colspan:2\ncell:E10:t:[Name]:f:1:cf:2:colspan:4\ncell:I10:b::2:::l:3:f:7\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:E11:t:[Street Address]:f:1:colspan:4\ncell:I11:b::2:::l:3:f:7\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:E12:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:I12:b::2:::l:3:f:7\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:Phone\\c :f:1:cf:2:colspan:2\ncell:E13:t:Phone\\c :f:1:cf:2:colspan:4\ncell:I13:b::2:::l:3:f:7\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::::2:l:3:f:3:cf:2\ncell:C14:b:::2::l:1:f:7\ncell:D14:b:::2::l:3:f:3:cf:2\ncell:E14:b:::2::l:1:f:7\ncell:F14:b:::2::l:1:f:7\ncell:G14:b:::2::l:1:f:7\ncell:H14:b:::2::l:1:f:7\ncell:I14:b::2:::l:3:f:7\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::2::2:l:3:f:7\ncell:C15:t:Description:b:1:1:1:1:f:2:cf:1:colspan:3\ncell:D15:t:Description:b:2::2:2:l:3:f:3:cf:2:colspan:2:rowspan:1\ncell:E15:t::b:2:2:2::l:1:f:7\ncell:F15:t:Hours:b:1:1:1:1:f:2:cf:1\ncell:G15:t:Rate:b:1:1:1:1:f:2:cf:1\ncell:H15:t:Amount:b:1:1:1:1:f:2:cf:1\ncell:I15:b::2::2:l:3:f:7\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::2::2:l:3:f:7\ncell:C16:b:1:1::1:f:1:cf:2:colspan:3\ncell:D16:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:E16:t::b:2:2:2::l:1:f:7\ncell:F16:b:1:1:::f:1:ntvf:1\ncell:G16:b:1:1:::f:1:ntvf:1\ncell:H16:vtf:t::IF(F16*G16>0,F16*G16,""):b:1:1:::f:1:ntvf:1\ncell:I16:b::2::2:l:3:f:7\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::2::2:l:3:f:7:cf:2\ncell:C17:b::1::1:f:1:cf:2:colspan:3\ncell:D17:b::1::1:l:1:f:7:colspan:2\ncell:E17:l:1:f:7\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::1:::f:1:ntvf:1\ncell:H17:vtf:t::IF(F17*G17>0,F17*G17,""):b::1:::f:1:ntvf:1\ncell:I17:b::2::2:l:3:f:7\ncell:A18:b::2:::l:3:f:7\ncell:B18:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C18:b::1::1:f:1:cf:2:colspan:3\ncell:D18:b::1::1:colspan:2\ncell:F18:b::1:::f:1:ntvf:1\ncell:G18:b::1:::f:1:ntvf:1\ncell:H18:vtf:t::IF(F18*G18>0,F18*G18,""):b::1:::f:1:ntvf:1\ncell:I18:b::2::2:l:3:f:7\ncell:A19:b::2:::l:3:f:7\ncell:B19:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C19:b::1::1:f:1:cf:2:colspan:3\ncell:D19:b::1::1:colspan:2\ncell:F19:b::1:::f:1:ntvf:1\ncell:G19:b::1:::f:1:ntvf:1\ncell:H19:vtf:t::IF(F19*G19>0,F19*G19,""):b::1:::f:1:ntvf:1\ncell:I19:b::2::2:l:3:f:7\ncell:A20:b::2:::l:3:f:7\ncell:B20:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C20:b::1::1:f:1:cf:2:colspan:3\ncell:D20:b::1::1:colspan:2\ncell:F20:b::1:::f:1:ntvf:1\ncell:G20:b::1:::f:1:ntvf:1\ncell:H20:vtf:t::IF(F20*G20>0,F20*G20,""):b::1:::f:1:ntvf:1\ncell:I20:b::2::2:l:3:f:7\ncell:A21:b::2:::l:3:f:7\ncell:B21:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C21:b::1::1:f:1:cf:2:colspan:3\ncell:D21:b::1::1:colspan:2\ncell:F21:b::1:::f:1:ntvf:1\ncell:G21:b::1:::f:1:ntvf:1\ncell:H21:vtf:t::IF(F21*G21>0,F21*G21,""):b::1:::f:1:ntvf:1\ncell:I21:b::2::2:l:3:f:7\ncell:A22:b::2:::l:3:f:7\ncell:B22:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C22:b::1::1:f:1:cf:2:colspan:3\ncell:D22:b::1::1:colspan:2\ncell:F22:b::1:::f:1:ntvf:1\ncell:G22:b::1:::f:1:ntvf:1\ncell:H22:vtf:t::IF(F22*G22>0,F22*G22,""):b::1:::f:1:ntvf:1\ncell:I22:b::2::2:l:3:f:7\ncell:A23:b::2:::l:3:f:7\ncell:B23:b::2::2:l:3:f:7:cf:2\ncell:C23:b::1::1:f:1:cf:2:colspan:3\ncell:D23:b::1::1:colspan:2\ncell:F23:b::1:::f:1:ntvf:1\ncell:G23:b::1:::f:1:ntvf:1\ncell:H23:vtf:t::IF(F23*G23>0,F23*G23,""):b::1:::f:1:ntvf:1\ncell:I23:b::2::2:l:3:f:7\ncell:A24:b::2:::l:3:f:7\ncell:B24:b::2::2:l:3:f:7:cf:2\ncell:C24:b::1::1:f:1:cf:2:colspan:3\ncell:D24:b::1::1:colspan:2\ncell:F24:b::1:::f:1:ntvf:1\ncell:G24:b::1:::f:1:ntvf:1\ncell:H24:vtf:t::IF(F24*G24>0,F24*G24,""):b::1:::f:1:ntvf:1\ncell:I24:b::2::2:l:3:f:7\ncell:A25:b::2:::l:3:f:7\ncell:B25:b::2::2:l:3:f:7:cf:2\ncell:C25:b::1::1:f:1:cf:2:colspan:3\ncell:D25:b::1::1:colspan:2\ncell:F25:b::1:::f:1:ntvf:1\ncell:G25:b::1:::f:1:ntvf:1\ncell:H25:vtf:t::IF(F25*G25>0,F25*G25,""):b::1:::f:1:ntvf:1\ncell:I25:b::2::2:l:3:f:7\ncell:A26:b::2:::l:3:f:7\ncell:B26:b::2::2:l:3:f:7:cf:2\ncell:C26:b::1::1:f:1:cf:2:colspan:3\ncell:D26:b::1::1:colspan:2\ncell:F26:b::1:::f:1:ntvf:1\ncell:G26:b::1:::f:1:ntvf:1\ncell:H26:vtf:t::IF(F26*G26>0,F26*G26,""):b::1:::f:1:ntvf:1\ncell:I26:b::2::2:l:3:f:7\ncell:A27:b::2:::l:3:f:7\ncell:B27:b::2::2:l:3:f:7:cf:2\ncell:C27:b::1::1:f:1:cf:2:colspan:3\ncell:D27:b::1::1:colspan:2\ncell:F27:b::1:::f:1:ntvf:1\ncell:G27:b::1:::f:1:ntvf:1\ncell:H27:vtf:t::IF(F27*G27>0,F27*G27,""):b::1:::f:1:ntvf:1\ncell:I27:b::2::2:l:3:f:7\ncell:A28:b::2:::l:3:f:4\ncell:B28:b::2::2:l:3:f:7:cf:2\ncell:C28:b::1:1:1:f:1:cf:2:colspan:3\ncell:D28:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:E28:b:::2::l:3:f:7:cf:2\ncell:F28:b::1:1::f:1:ntvf:1\ncell:G28:b::1:1::f:1:ntvf:1\ncell:H28:vtf:t::IF(F28*G28>0,F28*G28,""):b::1:::f:1:ntvf:1\ncell:I28:b::2::2:l:3:f:7\ncell:A29:b::2:::l:3:f:3\ncell:B29:b::2::2:l:3:f:3\ncell:C29:t:TOTAL:b:1:1:1:1:l:3:f:5:cf:2:colspan:5\ncell:D29:b:2::2::l:3:f:8:cf:2\ncell:E29:b:2:2:2::l:3:f:8:cf:2\ncell:F29:vtf:n:0:SUM(F16\\cF28):b:2:2:2::l:3:f:4:ntvf:2\ncell:G29:b:2:2:2::l:3:f:4:ntvf:2\ncell:H29:vtf:n:0:SUM(H16\\cH28):b:1:1:1::f:5:ntvf:1\ncell:I29:b::2::2:l:3:f:4\ncell:A30:b::2:::l:3:f:7\ncell:B30:b::::2:l:3:f:3:cf:2\ncell:C30:b:2::::l:1:f:7\ncell:D30:b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:7\ncell:F30:b:2::::l:1:f:7\ncell:G30:b:2::::l:1:f:7\ncell:H30:b:2::::l:1:f:7\ncell:I30:b::2:::l:3:f:3\ncell:A31:b::2:::l:3:f:7\ncell:B31:b::::2:l:3:f:7\ncell:D31:tvf:5:rowspan:4\ncell:I31:b::2:::l:3:f:7\ncell:A32:b::2:::l:3:f:7\ncell:B32:b::::2:l:3:f:7\ncell:I32:b::2:::l:3:f:7\ncell:A33:b::2:::l:3:f:7\ncell:B33:b::::2:l:3:f:7\ncell:I33:b::2:::l:3:f:7\ncell:A34:b::2:::l:3:f:7\ncell:B34:b::::2:l:3:f:7\ncell:I34:b::2:::l:3:f:7\ncell:A35:b::2:::l:3:f:7\ncell:B35:b:::2:2:l:3:f:7\ncell:C35:b:::2::l:3:f:7\ncell:D35:b:::2::l:3:f:7\ncell:E35:b:::2::l:3:f:7\ncell:F35:b:::2::l:3:f:7\ncell:G35:b:::2::l:3:f:7\ncell:H35:b:::2::l:3:f:7\ncell:I35:b::2:2::l:3:f:7\ncell:B36:b:1:::\ncell:C36:b:1:::\ncell:D36:b:1:::\ncell:E36:b:1:::\ncell:F36:b:1:::\ncell:G36:b:1:::\ncell:H36:b:1:::\ncell:I36:b:1:::\ncol:A:w:26\ncol:B:w:28\ncol:C:w:96\ncol:D:w:203\ncol:E:w:51\ncol:F:w:50\ncol:G:w:58\ncol:H:w:80\ncol:I:w:28\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:15.75\nrow:29:h:15.75\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:9:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:normal bold * Trebuchet MS\nfont:3:normal bold 10pt Arial\nfont:4:normal bold 12pt Arial\nfont:5:normal bold 12pt Trebuchet MS\nfont:6:normal bold 14pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 12pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00;(#,##0.00)\nvalueformat:3:d-mmm\nvalueformat:4:m/d/yy\nvalueformat:5:text-html\n', - }, - name: "typeii", - hidden: "0", - }, - }, - EditableCells: { - allow: true, - cells: { - "typei!C10": true, - "typei!B2": true, - "typei!C11": true, - "typei!C12": true, - "typei!C13": true, - "typei!D9": true, - "typei!D4": true, - "typei!D6": true, - "typei!E10": true, - "typei!E11": true, - "typei!E12": true, - "typei!E13": true, - "typei!F9": true, - "typei!C16": true, - "typei!C17": true, - "typei!C18": true, - "typei!C19": true, - "typei!C20": true, - "typei!C21": true, - "typei!C22": true, - "typei!C23": true, - "typei!C24": true, - "typei!C25": true, - "typei!C26": true, - "typei!C27": true, - "typei!C28": true, - "typei!F16": true, - "typei!F17": true, - "typei!F18": true, - "typei!F19": true, - "typei!F20": true, - "typei!F21": true, - "typei!F22": true, - "typei!F23": true, - "typei!F24": true, - "typei!F25": true, - "typei!F26": true, - "typei!F27": true, - "typei!F28": true, - "typei!E35": true, - "typeii!B2": true, - "typeii!D4": true, - "typeii!C10": true, - "typeii!C11": true, - "typeii!C12": true, - "typeii!C13": true, - "typeii!D9": true, - "typeii!E10": true, - "typeii!E11": true, - "typeii!E12": true, - "typeii!E13": true, - "typeii!F9": true, - "typeii!C16": true, - "typeii!C17": true, - "typeii!C18": true, - "typeii!C19": true, - "typeii!C20": true, - "typeii!C21": true, - "typeii!C22": true, - "typeii!C23": true, - "typeii!C24": true, - "typeii!C25": true, - "typeii!C26": true, - "typeii!C27": true, - "typeii!C28": true, - "typeii!F16": true, - "typeii!F17": true, - "typeii!F18": true, - "typeii!F19": true, - "typeii!F20": true, - "typeii!F21": true, - "typeii!F22": true, - "typeii!F23": true, - "typeii!F24": true, - "typeii!F25": true, - "typeii!F26": true, - "typeii!F27": true, - "typeii!F28": true, - "typeii!G16": true, - "typeii!G17": true, - "typeii!G18": true, - "typeii!G19": true, - "typeii!G20": true, - "typeii!G21": true, - "typeii!G22": true, - "typeii!G23": true, - "typeii!G24": true, - "typeii!G25": true, - "typeii!G26": true, - "typeii!G27": true, - "typeii!G28": true, - "typeii!E35": true, - "typeiii!G9": true, - "typeiii!G10": true, - "typeiii!B2": true, - "typeiii!B3": true, - "typeiii!B4": true, - "typeiii!B5": true, - "typeiii!B6": true, - "typeiii!B7": true, - "typeiii!B8": true, - "typeiii!B9": true, - "typeiii!B11": true, - "typeiii!B12": true, - "typeiii!B13": true, - "typeiii!B14": true, - "typeiii!B15": true, - "typeiii!B18": true, - "typeiii!B19": true, - "typeiii!B20": true, - "typeiii!B21": true, - "typeiii!B22": true, - "typeiii!B23": true, - "typeiii!B24": true, - "typeiii!B25": true, - "typeiii!B26": true, - "typeiii!B27": true, - "typeiii!B28": true, - "typeiii!B29": true, - "typeiii!G18": true, - "typeiii!G19": true, - "typeiii!G20": true, - "typeiii!G21": true, - "typeiii!G22": true, - "typeiii!G23": true, - "typeiii!G24": true, - "typeiii!G25": true, - "typeiii!G26": true, - "typeiii!G27": true, - "typeiii!G28": true, - "typeiii!G29": true, - "typeiii!B32": true, - "typeiii!B33": true, - "typeiii!B34": true, - "typeiii!G31": true, - "typeiii!G33": true, - "typeiii!B37": true, - "typeiv!G9": true, - "typeiv!G10": true, - "typeiv!B2": true, - "typeiv!B3": true, - "typeiv!B4": true, - "typeiv!B5": true, - "typeiv!B6": true, - "typeiv!B7": true, - "typeiv!B8": true, - "typeiv!B11": true, - "typeiv!B12": true, - "typeiv!B13": true, - "typeiv!B14": true, - "typeiv!B15": true, - "typeiv!B16": true, - "typeiv!B18": true, - "typeiv!B19": true, - "typeiv!B20": true, - "typeiv!B21": true, - "typeiv!B22": true, - "typeiv!B23": true, - "typeiv!B24": true, - "typeiv!B25": true, - "typeiv!B26": true, - "typeiv!B27": true, - "typeiv!B28": true, - "typeiv!B29": true, - "typeiv!E18": true, - "typeiv!E19": true, - "typeiv!E20": true, - "typeiv!E21": true, - "typeiv!E22": true, - "typeiv!E23": true, - "typeiv!E24": true, - "typeiv!E25": true, - "typeiv!E26": true, - "typeiv!E27": true, - "typeiv!E28": true, - "typeiv!E29": true, - "typeiv!F18": true, - "typeiv!F19": true, - "typeiv!F2": true, - "typeiv!F20": true, - "typeiv!F21": true, - "typeiv!F22": true, - "typeiv!F23": true, - "typeiv!F24": true, - "typeiv!F25": true, - "typeiv!F26": true, - "typeiv!F27": true, - "typeiv!F28": true, - "typeiv!F29": true, - "typeiv!B32": true, - "typeiv!B33": true, - "typeiv!B34": true, - "typeiv!G31": true, - "typeiv!G33": true, - "typeiv!B37": true, - "typeii!F15": true, - "typeii!G15": true, - "typeiv!E17": true, - "typeiv!F17": true, - "typeii!D6": true, - "typeiv!G11": true, - "typei!C4": true, - "typeii!C4": true, - "typeiii!F10": true, - "typeiv!F11": true, - "typeiv!F10": true, - "typeiii!F9": true, - "typeii!C6": true, - "typei!C6": true, - "typei!F4": true, - "typeii!F4": true, - "typeiii!F4": true, - "typeiii!F2": true, - "typeiv!F4": true, - "typei!F29": true, - "typeii!H29": true, - "typeiii!G30": true, - "typeiii!G34": true, - "typeiv!G30": true, - "typeiv!G34": true, - }, - constraints: {}, - }, - }, - footers: [ - { name: "Invoice 1", index: 1, isActive: true }, - { name: "Invoice 2", index: 2, isActive: false }, - ], - logoCell: null, - signatureCell: null, - cellMappings: {}, - }, - 4: { - template: "Web Invoice 2", - templateId: 4, - msc: { - numsheets: 2, - currentid: "sheet3", - currentname: "typeiii", - sheetArr: { - sheet3: { - sheetstr: { - savestr: - "version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:3:cf:2:colspan:3\ncell:C2:t::l:2:f:9\ncell:D2:t::l:2:f:9\ncell:E2:l:1:f:7:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:9\ncell:B3:t:[Company Slogan]:f:4:cf:2:colspan:3\ncell:C3:t::l:2:f:9\ncell:D3:t::l:2:f:9\ncell:B4:f:2:colspan:2\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:2\ncell:F5:l:1:f:6\ncell:G5:l:1:f:10:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:G6:l:1:f:9\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:2\ncell:B8:t:Email\\c:f:1:cf:2:colspan:2\ncell:B9:colspan:2\ncell:F9:t:DATE \\c:l:1:f:6:cf:2\ncell:G9:l:1:f:10:cf:2:ntvf:3\ncell:B10:t:BILL TO\\c:f:5:c:1:bg:3:cf:2:colspan:2\ncell:F10:t:INVOICE # \\c:l:1:f:6:cf:2\ncell:G10:v:1:l:1:f:10:cf:2\ncell:B11:t:[Name]:f:1:cf:2:colspan:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:2\ncell:F12:t: \ncell:B13:t:[Street Address]:f:1:cf:2:colspan:2\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:2\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:6:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C17:t::l:2:f:9\ncell:D17:t::l:2:f:9\ncell:E17:t::l:2:f:9\ncell:F17:t::l:2:f:9\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:6:c:1:bg:3:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C18:t::l:2:f:9\ncell:D18:t::l:2:f:9\ncell:E18:t::l:2:f:9\ncell:F18:t::b::2:::l:1:f:9\ncell:G18:b::1::1:f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C19:t::l:2:f:9\ncell:D19:t::l:2:f:9\ncell:E19:t::l:2:f:9\ncell:F19:t::b::2:::l:1:f:9\ncell:G19:b::1::1:f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C20:t::l:2:f:9\ncell:D20:t::l:2:f:9\ncell:E20:t::l:2:f:9\ncell:F20:t::b::2:::l:1:f:9\ncell:G20:b::1::1:f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C21:t::l:2:f:9\ncell:D21:t::l:2:f:9\ncell:E21:t::l:2:f:9\ncell:F21:t::b::2:::l:1:f:9\ncell:G21:b::1::1:f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C22:t::l:2:f:9\ncell:D22:t::l:2:f:9\ncell:E22:t::l:2:f:9\ncell:F22:t::b::2:::l:1:f:9\ncell:G22:b::1::1:f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:9\ncell:D23:t::l:2:f:9\ncell:E23:t::l:2:f:9\ncell:F23:t::b::2:::l:1:f:9\ncell:G23:b::1::1:f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:9\ncell:D24:t::l:2:f:9\ncell:E24:t::l:2:f:9\ncell:F24:t::b::2:::l:1:f:9\ncell:G24:b::1::1:f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:9\ncell:D25:t::l:2:f:9\ncell:E25:t::l:2:f:9\ncell:F25:t::b::2:::l:1:f:9\ncell:G25:b::1::1:f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:9\ncell:D26:t::l:2:f:9\ncell:E26:t::l:2:f:9\ncell:F26:t::b::2:::l:1:f:9\ncell:G26:b::1::1:f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:9\ncell:D27:t::l:2:f:9\ncell:E27:t::l:2:f:9\ncell:F27:t::b::2:::l:1:f:9\ncell:G27:b::1::1:f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:9\ncell:D28:t::l:2:f:9\ncell:E28:t::l:2:f:9\ncell:F28:t::b::2:::l:1:f:9\ncell:G28:b::1::1:f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:5:rowspan:1\ncell:C29:t::b:::2::l:1:f:9\ncell:D29:t::b:::2::l:1:f:9\ncell:E29:t::b:::2::l:1:f:9\ncell:F29:t::b::2:2::l:1:f:9\ncell:G29:b::1:1:1:f:1:ntvf:1\ncell:B30:b:2::::l:1:f:9\ncell:C30:b:2::::l:1:f:9\ncell:D30:b:2::::l:1:f:9\ncell:E30:b:2::::l:1:f:10\ncell:F30:t:Subtotal:b:2::::l:1:f:10\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:8:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:6:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:9\ncell:D31:t::b:::2::l:1:f:9\ncell:F31:t:Tax Rate:l:1:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3\ncell:C32:t::b:2::::l:1:f:9\ncell:D32:t::b:2::::l:1:f:9\ncell:F32:t:Tax:l:1:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3\ncell:C33:t::l:2:f:9\ncell:D33:t::l:2:f:9\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:9\ncell:D34:t::l:2:f:9\ncell:F34:t:TOTAL:b:2::::l:1:f:6\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:5:ntvf:1\ncell:C36:tvf:4:colspan:5:rowspan:7\ncol:A:w:40\ncol:B:w:232\ncol:C:w:53\ncol:D:w:90\ncol:E:w:54\ncol:F:w:91\ncol:G:w:99\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nsheet:c:7:r:36:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 16pt Trebuchet MS\nfont:4:italic normal * Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 28pt Trebuchet MS\nfont:8:normal normal * Trebuchet MS\nfont:9:normal normal 10pt Arial\nfont:10:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", - }, - name: "typeiii", - hidden: "0", - }, - sheet4: { - sheetstr: { - savestr: - 'version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:2:cf:2:colspan:3:rowspan:1\ncell:C2:t::l:2:f:7\ncell:D2:t::l:2:f:7\ncell:F2:t:INVOICE:l:1:f:6:c:1:cf:2:colspan:2\ncell:G2:t::l:2:f:7\ncell:B3:t:[Company slogan]:f:3:cf:2:colspan:3:rowspan:1\ncell:C3:t::l:2:f:7\ncell:D3:t::l:2:f:7\ncell:B4:cf:2:colspan:3:rowspan:1\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:F5:l:1:f:5\ncell:G5:l:1:f:8:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:G6:l:1:f:7\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B8:t:Email\\c:f:1:cf:2:colspan:3:rowspan:1\ncell:B9:cf:2:colspan:3:rowspan:1\ncell:B10:t:BILL TO\\c:l:1:f:5:bg:2:cf:2:colspan:2\ncell:F10:t:DATE\\c:l:1:f:5:cf:2\ncell:G10:l:1:f:8:cf:2:ntvf:3\ncell:B11:t:[Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:F11:t:INVOICE # \\c:l:1:f:5:cf:2\ncell:G11:v:1:l:1:f:8:cf:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:J12:tvf:4\ncell:B13:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B16:cf:2:colspan:3:rowspan:1\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:5:bg:2:cf:1:colspan:3:rowspan:1\ncell:C17:t::b:1::1::l:1:f:7:bg:2\ncell:D17:t::b:1::1::l:1:f:7:bg:2\ncell:E17:t:HOURS:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:F17:t:RATE:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C18:t::b:2::::l:1:f:7\ncell:D18:t::b:2:2:::l:1:f:7\ncell:E18:b:1:1::1:f:1:ntvf:1\ncell:F18:b:1:1::1:f:1:ntvf:1\ncell:G18:vtf:t::IF(E18*F18>0,E18*F18,""):b:1:1:::f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C19:t::l:2:f:7\ncell:D19:t::b::2:::l:1:f:7\ncell:E19:b::1::1:f:1:ntvf:1\ncell:F19:b::1::1:f:1:ntvf:1\ncell:G19:vtf:t::IF(E19*F19>0,E19*F19,""):b::1:::f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C20:t::l:2:f:7\ncell:D20:t::b::2:::l:1:f:7\ncell:E20:b::1::1:f:1:ntvf:1\ncell:F20:b::1::1:f:1:ntvf:1\ncell:G20:vtf:t::IF(E20*F20>0,E20*F20,""):b::1:::f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C21:t::l:2:f:7\ncell:D21:t::b::2:::l:1:f:7\ncell:E21:b::1::1:f:1:ntvf:1\ncell:F21:b::1::1:f:1:ntvf:1\ncell:G21:vtf:t::IF(E21*F21>0,E21*F21,""):b::1:::f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C22:t::l:2:f:7\ncell:D22:t::b::2:::l:1:f:7\ncell:E22:b::1::1:f:1:ntvf:1\ncell:F22:b::1::1:f:1:ntvf:1\ncell:G22:vtf:t::IF(E22*F22>0,E22*F22,""):b::1:::f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C23:t::l:2:f:7\ncell:D23:t::b::2:::l:1:f:7\ncell:E23:b::1::1:f:1:ntvf:1\ncell:F23:b::1::1:f:1:ntvf:1\ncell:G23:vtf:t::IF(E23*F23>0,E23*F23,""):b::1:::f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C24:t::l:2:f:7\ncell:D24:t::b::2:::l:1:f:7\ncell:E24:b::1::1:f:1:ntvf:1\ncell:F24:b::1::1:f:1:ntvf:1\ncell:G24:vtf:t::IF(E24*F24>0,E24*F24,""):b::1:::f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C25:t::l:2:f:7\ncell:D25:t::b::2:::l:1:f:7\ncell:E25:b::1::1:f:1:ntvf:1\ncell:F25:b::1::1:f:1:ntvf:1\ncell:G25:vtf:t::IF(E25*F25>0,E25*F25,""):b::1:::f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C26:t::l:2:f:7\ncell:D26:t::b::2:::l:1:f:7\ncell:E26:b::1::1:f:1:ntvf:1\ncell:F26:b::1::1:f:1:ntvf:1\ncell:G26:vtf:t::IF(E26*F26>0,E26*F26,""):b::1:::f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C27:t::l:2:f:7\ncell:D27:t::b::2:::l:1:f:7\ncell:E27:b::1::1:f:1:ntvf:1\ncell:F27:b::1::1:f:1:ntvf:1\ncell:G27:vtf:t::IF(E27*F27>0,E27*F27,""):b::1:::f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C28:t::l:2:f:7\ncell:D28:t::b::2:::l:1:f:7\ncell:E28:b::1::1:f:1:ntvf:1\ncell:F28:b::1::1:f:1:ntvf:1\ncell:G28:vtf:t::IF(E28*F28>0,E28*F28,""):b::1:::f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:3:rowspan:1\ncell:C29:t::b:::2::l:1:f:7\ncell:D29:t::b::2:2::l:1:f:7\ncell:E29:b::1:1:1:f:1:ntvf:1\ncell:F29:b::1:1:1:f:1:ntvf:1\ncell:G29:vtf:t::IF(E29*F29>0,E29*F29,""):b::1:::f:1:ntvf:1\ncell:B30:b:2::::l:1:f:8:cf:1:colspan:3:rowspan:1\ncell:C30:t::b:2::::l:1:f:7\ncell:D30:t::b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:8\ncell:F30:t:Subtotal:b:1::::f:1\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:1:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:5:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:7\ncell:D31:t::b:::2::l:1:f:7\ncell:F31:t:Tax Rate:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3:rowspan:1\ncell:C32:t::b:2::::l:1:f:7\ncell:D32:t::b:2::::l:1:f:7\ncell:F32:t:Tax:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3:rowspan:1\ncell:C33:t::l:2:f:7\ncell:D33:t::l:2:f:7\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:7\ncell:D34:t::l:2:f:7\ncell:F34:t:TOTAL:b:1::::l:1:f:4\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:4:ntvf:1\ncell:B35:b:2::::l:1:f:7\ncell:C35:b:2::::l:1:f:7\ncell:D35:b:2::::l:1:f:7\ncell:C36:tvf:4:colspan:5:rowspan:7\ncol:A:w:40\ncol:B:w:194\ncol:C:w:128\ncol:D:w:60\ncol:E:w:65\ncol:F:w:95\ncol:G:w:90\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:10:r:36:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncolor:1:rgb(0,0,0)\ncolor:2:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 16pt Trebuchet MS\nfont:3:italic normal * Trebuchet MS\nfont:4:normal bold * Trebuchet MS\nfont:5:normal bold 10pt Trebuchet MS\nfont:6:normal bold 28pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', - }, - name: "typeiv", - hidden: "0", - }, - }, - EditableCells: { - allow: true, - cells: { - "typei!C10": true, - "typei!B2": true, - "typei!C11": true, - "typei!C12": true, - "typei!C13": true, - "typei!D9": true, - "typei!D4": true, - "typei!D6": true, - "typei!E10": true, - "typei!E11": true, - "typei!E12": true, - "typei!E13": true, - "typei!F9": true, - "typei!C16": true, - "typei!C17": true, - "typei!C18": true, - "typei!C19": true, - "typei!C20": true, - "typei!C21": true, - "typei!C22": true, - "typei!C23": true, - "typei!C24": true, - "typei!C25": true, - "typei!C26": true, - "typei!C27": true, - "typei!C28": true, - "typei!F16": true, - "typei!F17": true, - "typei!F18": true, - "typei!F19": true, - "typei!F20": true, - "typei!F21": true, - "typei!F22": true, - "typei!F23": true, - "typei!F24": true, - "typei!F25": true, - "typei!F26": true, - "typei!F27": true, - "typei!F28": true, - "typei!E35": true, - "typeii!B2": true, - "typeii!D4": true, - "typeii!C10": true, - "typeii!C11": true, - "typeii!C12": true, - "typeii!C13": true, - "typeii!D9": true, - "typeii!E10": true, - "typeii!E11": true, - "typeii!E12": true, - "typeii!E13": true, - "typeii!F9": true, - "typeii!C16": true, - "typeii!C17": true, - "typeii!C18": true, - "typeii!C19": true, - "typeii!C20": true, - "typeii!C21": true, - "typeii!C22": true, - "typeii!C23": true, - "typeii!C24": true, - "typeii!C25": true, - "typeii!C26": true, - "typeii!C27": true, - "typeii!C28": true, - "typeii!F16": true, - "typeii!F17": true, - "typeii!F18": true, - "typeii!F19": true, - "typeii!F20": true, - "typeii!F21": true, - "typeii!F22": true, - "typeii!F23": true, - "typeii!F24": true, - "typeii!F25": true, - "typeii!F26": true, - "typeii!F27": true, - "typeii!F28": true, - "typeii!G16": true, - "typeii!G17": true, - "typeii!G18": true, - "typeii!G19": true, - "typeii!G20": true, - "typeii!G21": true, - "typeii!G22": true, - "typeii!G23": true, - "typeii!G24": true, - "typeii!G25": true, - "typeii!G26": true, - "typeii!G27": true, - "typeii!G28": true, - "typeii!E35": true, - "typeiii!G9": true, - "typeiii!G10": true, - "typeiii!B2": true, - "typeiii!B3": true, - "typeiii!B4": true, - "typeiii!B5": true, - "typeiii!B6": true, - "typeiii!B7": true, - "typeiii!B8": true, - "typeiii!B9": true, - "typeiii!B11": true, - "typeiii!B12": true, - "typeiii!B13": true, - "typeiii!B14": true, - "typeiii!B15": true, - "typeiii!B18": true, - "typeiii!B19": true, - "typeiii!B20": true, - "typeiii!B21": true, - "typeiii!B22": true, - "typeiii!B23": true, - "typeiii!B24": true, - "typeiii!B25": true, - "typeiii!B26": true, - "typeiii!B27": true, - "typeiii!B28": true, - "typeiii!B29": true, - "typeiii!G18": true, - "typeiii!G19": true, - "typeiii!G20": true, - "typeiii!G21": true, - "typeiii!G22": true, - "typeiii!G23": true, - "typeiii!G24": true, - "typeiii!G25": true, - "typeiii!G26": true, - "typeiii!G27": true, - "typeiii!G28": true, - "typeiii!G29": true, - "typeiii!B32": true, - "typeiii!B33": true, - "typeiii!B34": true, - "typeiii!G31": true, - "typeiii!G33": true, - "typeiii!B37": true, - "typeiv!G9": true, - "typeiv!G10": true, - "typeiv!B2": true, - "typeiv!B3": true, - "typeiv!B4": true, - "typeiv!B5": true, - "typeiv!B6": true, - "typeiv!B7": true, - "typeiv!B8": true, - "typeiv!B11": true, - "typeiv!B12": true, - "typeiv!B13": true, - "typeiv!B14": true, - "typeiv!B15": true, - "typeiv!B16": true, - "typeiv!B18": true, - "typeiv!B19": true, - "typeiv!B20": true, - "typeiv!B21": true, - "typeiv!B22": true, - "typeiv!B23": true, - "typeiv!B24": true, - "typeiv!B25": true, - "typeiv!B26": true, - "typeiv!B27": true, - "typeiv!B28": true, - "typeiv!B29": true, - "typeiv!E18": true, - "typeiv!E19": true, - "typeiv!E20": true, - "typeiv!E21": true, - "typeiv!E22": true, - "typeiv!E23": true, - "typeiv!E24": true, - "typeiv!E25": true, - "typeiv!E26": true, - "typeiv!E27": true, - "typeiv!E28": true, - "typeiv!E29": true, - "typeiv!F18": true, - "typeiv!F19": true, - "typeiv!F2": true, - "typeiv!F20": true, - "typeiv!F21": true, - "typeiv!F22": true, - "typeiv!F23": true, - "typeiv!F24": true, - "typeiv!F25": true, - "typeiv!F26": true, - "typeiv!F27": true, - "typeiv!F28": true, - "typeiv!F29": true, - "typeiv!B32": true, - "typeiv!B33": true, - "typeiv!B34": true, - "typeiv!G31": true, - "typeiv!G33": true, - "typeiv!B37": true, - "typeii!F15": true, - "typeii!G15": true, - "typeiv!E17": true, - "typeiv!F17": true, - "typeii!D6": true, - "typeiv!G11": true, - "typei!C4": true, - "typeii!C4": true, - "typeiii!F10": true, - "typeiv!F11": true, - "typeiv!F10": true, - "typeiii!F9": true, - "typeii!C6": true, - "typei!C6": true, - "typei!F4": true, - "typeii!F4": true, - "typeiii!F4": true, - "typeiii!F2": true, - "typeiv!F4": true, - "typei!F29": true, - "typeii!H29": true, - "typeiii!G30": true, - "typeiii!G34": true, - "typeiv!G30": true, - "typeiv!G34": true, - }, - constraints: {}, - }, - }, - footers: [ - { name: "Company Invoice 1", index: 1, isActive: true }, - { name: "Company Invoice 2", index: 2, isActive: false }, - ], - logoCell: null, - signatureCell: null, - cellMappings: {}, - }, -}; diff --git a/src/templates-meta.ts b/src/templates-meta.ts index 431f848..dd3ff0c 100644 --- a/src/templates-meta.ts +++ b/src/templates-meta.ts @@ -1,26 +1,75 @@ export let tempMeta = [ { - name: "Mobile Invoice 1", - template_id: 1, + name: "Mobile-Invoice-1", + template_id: 1001, ImageUri: "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", }, { - name: "Mobile Invoice 2", - template_id: 2, + name: "Mobile-Tax-Invoice", + template_id: 1002, ImageUri: "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", }, { - name: "Web Invoice 1", - template_id: 3, + name: "Mobile-Multi-Invoice", + template_id: 1003, ImageUri: "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", }, { - name: "Web Invoice 2", - template_id: 4, + name: "Web-Invoice-1", + template_id: 3001, ImageUri: "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", }, + { + name: "Web-Invoice-2", + template_id: 3002, + ImageUri: + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + }, + { + name: "Company-Invoice-1", + template_id: 3003, + ImageUri: + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + }, + { + name: "Company-Invoice-2", + template_id: 3004, + ImageUri: + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + }, + // { + // name: "Health-Log", + // template_id: 5001, + // ImageUri: + // "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + // }, + // { + // name: "Mobile-Health-Log", + // template_id: 5002, + // ImageUri: + // "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + // }, + + // { + // name: "Diet-Tracker", + // template_id: 5003, + // ImageUri: + // "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + // }, + // { + // name: "Mobile-Diet-Log", + // template_id: 5004, + // ImageUri: + // "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + // }, + // { + // name: "Tablet-Invoice-1", + // template_id: 2001, + // ImageUri: + // "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + // }, ]; diff --git a/src/templates-new.ts b/src/templates-new.ts deleted file mode 100644 index 711cc65..0000000 --- a/src/templates-new.ts +++ /dev/null @@ -1,189 +0,0 @@ -export let APP_NAME = "Invoice Suite"; - -// Template Interface for better type safety -export interface TemplateData { - template: string; - templateId: number; - msc: { - numsheets: number; - currentid: string; - currentname: string; - sheetArr: { - [sheetName: string]: { - sheetstr: { - savestr: string; - }; - name: string; - hidden: string; - }; - }; - EditableCells: { - allow: boolean; - cells: { - [cellName: string]: boolean; - }; - }; - Prompts: { - [cellName: string]: [string, string, string, string]; - }; - }; - footers: { - name: string; - index: number; - isActive: boolean; - }[]; - logoCell: string | null; - signatureCell: string | null; - cellMappings: { - [headingName: string]: { - [cellName: string]: { - heading: string; - datatype: string; - }; - }; - }; -} - -// Sample template metadata for demonstration -export const TEMPLATE_METADATA_SAMPLES: { - [key: number]: Pick< - TemplateData, - "footers" | "logoCell" | "signatureCell" | "cellMappings" - >; -} = { - 1: { - footers: [ - { name: "Detail1", index: 1, isActive: false }, - { name: "Detail2", index: 2, isActive: false }, - { name: "Invoice", index: 3, isActive: true }, - ], - logoCell: "F8", - signatureCell: null, - cellMappings: { - "Company Information": { - B8: { heading: "Company Name", datatype: "text" }, - B9: { heading: "Street Address", datatype: "text" }, - B10: { heading: "City, State, Zip", datatype: "text" }, - B11: { heading: "Phone", datatype: "text" }, - B12: { heading: "Email", datatype: "email" }, - }, - "Bill To": { - B15: { heading: "Customer Name", datatype: "text" }, - B16: { heading: "Customer Company", datatype: "text" }, - B17: { heading: "Customer Address", datatype: "text" }, - B18: { heading: "Customer City, State, Zip", datatype: "text" }, - B19: { heading: "Customer Phone", datatype: "text" }, - B20: { heading: "Customer Email", datatype: "email" }, - }, - "Line Items": { - B23: { heading: "Description", datatype: "text" }, - G23: { heading: "Amount", datatype: "decimal" }, - }, - }, - }, - 2: { - footers: [ - { name: "Invoice 1", index: 1, isActive: true }, - { name: "Invoice 2", index: 2, isActive: false }, - ], - logoCell: null, - signatureCell: null, - cellMappings: { - Header: { - B2: { heading: "Invoice Title", datatype: "text" }, - B5: { heading: "Invoice Number", datatype: "text" }, - F4: { heading: "Date", datatype: "date" }, - }, - "Line Items": { - C23: { heading: "Description", datatype: "text" }, - E23: { heading: "Quantity", datatype: "number" }, - F23: { heading: "Price", datatype: "decimal" }, - }, - }, - }, -}; - -// Simplified template data structure -export let DATA: { [key: number]: TemplateData } = { - 1: { - template: "Mobile Invoice 1", - templateId: 1, - msc: { - numsheets: 3, - currentid: "sheet3", - currentname: "sheet6", - sheetArr: { - // Existing sheet data would go here - keeping original structure - // For brevity, using a simplified version here - }, - EditableCells: { - allow: true, - cells: { - "sheet6!B2": true, - "sheet6!F4": true, - "sheet6!G4": true, - // ... other editable cells - }, - }, - Prompts: { - "sheet6!B2": ["prompttext", "0", "1e10", "Invoice"], - "sheet6!F4": ["prompttext", "0", "1e10", "Date"], - "sheet6!G4": ["prompttext", "0", "1e10", "Date"], - // ... other prompts - }, - }, - ...TEMPLATE_METADATA_SAMPLES[1], - }, - 2: { - template: "Mobile Invoice 2", - templateId: 2, - msc: { - numsheets: 2, - currentid: "sheet1", - currentname: "inv1", - sheetArr: { - // Existing sheet data would go here - }, - EditableCells: { - allow: true, - cells: { - "inv1!B2": true, - "inv1!C5": true, - // ... other editable cells - }, - }, - Prompts: { - "inv1!B2": ["prompttext", "0", "1e10", "Invoice"], - "inv1!C5": ["prompttext", "0", "1e10", "From"], - // ... other prompts - }, - }, - ...TEMPLATE_METADATA_SAMPLES[2], - }, -}; - -// Helper functions for template management -export const getTemplateMetadata = (templateId: number) => { - const template = DATA[templateId]; - if (!template) return null; - - return { - template: template.template, - templateId: template.templateId, - footers: template.footers, - logoCell: template.logoCell, - signatureCell: template.signatureCell, - cellMappings: template.cellMappings, - }; -}; - -export const getAvailableTemplates = () => { - return Object.keys(DATA).map((id) => ({ - id: parseInt(id), - name: DATA[parseInt(id)].template, - })); -}; - -export const getTemplateById = (templateId: number) => { - return DATA[templateId] || null; -}; diff --git a/src/templates.ts b/src/templates.ts index f4ebc37..6834610 100644 --- a/src/templates.ts +++ b/src/templates.ts @@ -1,6 +1,25 @@ export let APP_NAME = "Invoice Suite"; // Template Interface for better type safety +export interface ItemsConfig { + Name: string; + Rows: { + start: number; + end: number; + }; + Columns: { + [columnName: string]: string; // Column name to cell column mapping + }; +} + +export interface NestedField { + [key: string]: string | NestedField; +} + +export interface CellMapping { + [fieldName: string]: string | ItemsConfig | NestedField; +} + export interface TemplateData { template: string; templateId: number; @@ -35,422 +54,235 @@ export interface TemplateData { logoCell: string | { [footerIndex: number]: string }; signatureCell: string | { [footerIndex: number]: string }; cellMappings: { - [footerIndex: number]: { - [fieldName: string]: - | string - | { [subField: string]: any } - | { - name?: string; - Range?: { - start: number; - end: number; - }; - Content?: { - [fieldName: string]: string; - }; - }; - }; + [footerIndex: number]: CellMapping; }; } export let DATA: { [key: number]: TemplateData } = { - 1: { - template: "Mobile Invoice 1", - templateId: 1, + 1001: { + template: "Mobile-Invoice-1", + templateId: 1001, + footers: [{ name: "Invoice", index: 1, isActive: true }], + logoCell: { + 1: "F5", + }, + signatureCell: { + 1: "D38", + }, + cellMappings: { + 1: { + Heading: "B2", + Items: { + Name: "Items", + Rows: { + start: 23, + end: 35, + }, + Columns: { + Description: "C", + Amount: "F", + }, + }, + + Date: "D20", + InvoiceNumber: "C18", + From: { + Name: "C12", + StreetAddress: "C13", + CityStateZip: "C14", + Phone: "C15", + Email: "C16", + }, + BillTo: { + Name: "C5", + StreetAddress: "C6", + CityStateZip: "C7", + Phone: "C8", + Email: "C9", + }, + }, + }, + msc: { - numsheets: 3, - currentid: "sheet3", - currentname: "sheet6", + numsheets: 1, + currentid: "sheet1", + currentname: "inv1", sheetArr: { sheet1: { sheetstr: { savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Hours:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Rate:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + "version:1.5\ncell:B1:b:::2::l:1:f:10\ncell:C1:b:::2::l:1:f:10\ncell:D1:b:::2::l:1:f:10\ncell:E1:b:::2::l:1:f:10\ncell:F1:b:::2::l:1:f:10\ncell:G1:b:::2::l:1:f:10\ncell:A2:b::2:::l:1:f:10\ncell:B2:t:INVOICE:b:1:1:1:1:f:13:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:10\ncell:D2:t::b:2::2::l:1:f:10\ncell:E2:t::b:2::2::l:1:f:10\ncell:F2:t::b:2::2::l:1:f:10\ncell:G2:t::b:2::2::l:1:f:10\ncell:A3:b::2:::l:3:f:10\ncell:B3:b:2:::2:l:3:f:10\ncell:C3:b:2::::l:1:f:10\ncell:D3:b:2::::l:1:f:10\ncell:E3:b:2::::l:1:f:10\ncell:F3:b:2::::l:1:f:10\ncell:G3:b:2:2:::l:3:f:10\ncell:A4:b::2:::l:3:f:10\ncell:B4:b::::2:l:3:f:6\ncell:C4:t:BILL TO\\c:f:9:colspan:2\ncell:E4:f:5:cf:2\ncell:G4:b::2:::l:3:f:10\ncell:A5:b::2:::l:3:f:10\ncell:B5:b::::2:l:3:f:7\ncell:C5:t:[Name]:f:9:cf:2:colspan:3\ncell:E5:f:1:cf:2\ncell:F5:t::l:2:f:10:tvf:4:rowspan:3\ncell:G5:t::b::1:::l:2:f:10\ncell:A6:b::2:::l:3:f:10\ncell:B6:b::::2:l:3:f:7\ncell:C6:t:[Street Address]:f:9:cf:2:colspan:3\ncell:E6:f:1:cf:2\ncell:F6:l:2:f:10\ncell:G6:b::1:::l:2:f:10\ncell:A7:b::2:::l:3:f:10\ncell:B7:b::::2:l:3:f:7\ncell:C7:t:[City, State, Zip]:f:9:cf:2:colspan:3\ncell:E7:f:1:cf:2\ncell:G7:b::2:::l:3:f:10\ncell:A8:b::2:::l:3:f:10\ncell:B8:b::::2:l:3:f:7\ncell:C8:t:Phone\\c:f:9:cf:2:colspan:3\ncell:D8:f:8:cf:2\ncell:E8:f:1:cf:2\ncell:G8:b::2:::l:3:f:10\ncell:A9:b::2:::l:3:f:10\ncell:B9:b::::2:l:3:f:7\ncell:C9:t:Email\\c:f:9:cf:2:colspan:3\ncell:E9:f:1:cf:2\ncell:G9:b::2:::l:3:f:10\ncell:A10:b::2:::l:3:f:10\ncell:B10:b::::2:l:3:f:7\ncell:C10:colspan:2\ncell:G10:b::2:::l:3:f:10\ncell:A11:b::2:::l:3:f:10\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:FROM\\c:f:9:colspan:2\ncell:E11:f:8:colspan:2\ncell:G11:b::2:::l:3:f:10\ncell:A12:b::2:::l:3:f:10\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[Name]:f:9:colspan:4\ncell:E12:colspan:2\ncell:G12:b::2:::l:3:f:10\ncell:A13:b::2:::l:3:f:10\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:[Street Address]:f:9:colspan:4\ncell:E13:colspan:2\ncell:G13:b::2:::l:3:f:10\ncell:A14:b::2:::l:3:f:10\ncell:B14:b::::2:l:3:f:7\ncell:C14:t:[City, State, Zip]:f:9:colspan:4\ncell:E14:colspan:2\ncell:G14:b::2:::l:3:f:10\ncell:A15:b::2:::l:3:f:10\ncell:B15:b::::2:l:3:f:7\ncell:C15:t:Phone\\c:f:9:colspan:4\ncell:D15:f:8:cf:2:colspan:3\ncell:E15:colspan:2\ncell:G15:b::2:::l:3:f:10\ncell:A16:b::2:::l:3:f:10\ncell:B16:b::::2:l:3:f:7\ncell:C16:t:Email\\c:f:9:colspan:4\ncell:E16:colspan:2\ncell:G16:b::2:::l:3:f:10\ncell:A17:b::2:::l:3:f:10\ncell:B17:b::::2:l:3:f:6\ncell:G17:b::2:::l:3:f:10\ncell:A18:b::2:::l:3:f:10\ncell:B18:b::::2:l:3:f:10\ncell:C18:t:INVOICE #\\c:f:9:cf:2:colspan:4\ncell:D18:v:1:l:4:f:9:cf:2:colspan:3\ncell:G18:b::2:::l:3:f:10\ncell:A19:b::2:::l:3:f:10\ncell:B19:b::::2:l:3:f:10\ncell:G19:b::2:::l:3:f:10\ncell:A20:b::2:::l:3:f:10\ncell:B20:b::::2:l:3:f:10\ncell:C20:t:DATE\\c:f:9:cf:2\ncell:D20:vtf:nd:45898:TODAY():f:9:cf:2:ntvf:3:colspan:2\ncell:G20:b::2:::l:3:f:10\ncell:A21:b::2:::l:3:f:10\ncell:B21:b::::2:l:3:f:6:cf:2\ncell:C21:b:::2::l:1:f:10\ncell:D21:b:::2::l:3:f:6:cf:2\ncell:E21:b:::2::l:1:f:10\ncell:F21:b:::2::l:1:f:10\ncell:G21:b::2:::l:3:f:10\ncell:A22:b::2:::l:3:f:10\ncell:B22:b::2::2:l:3:f:10\ncell:C22:t:Description:b:1::1:1:f:9:cf:1:colspan:3\ncell:D22:t:Description:b:1::1::l:3:f:6:cf:2:colspan:2:rowspan:1\ncell:E22:t::b:2:2:2::l:1:f:10\ncell:F22:t:Amount:b:1:1:1:1:f:9:cf:1\ncell:G22:b::2::2:l:3:f:10\ncell:A23:b::2:::l:3:f:10\ncell:B23:b::2::2:l:3:f:10\ncell:C23:b:1:1::1:f:2:cf:2:colspan:3\ncell:D23:b:1:1:::l:3:f:10:cf:2:colspan:2:rowspan:1\ncell:E23:t::b:2:2:2::l:1:f:10\ncell:F23:b:1:1:::f:2:ntvf:1\ncell:G23:b::2::2:l:3:f:10\ncell:A24:b::2:::l:3:f:10\ncell:B24:b::2::2:l:3:f:10:cf:2\ncell:C24:b::1::1:f:2:cf:2:colspan:3\ncell:D24:b::1:::l:1:f:10:colspan:2\ncell:E24:l:1:f:10\ncell:F24:b::1:::f:2:ntvf:1\ncell:G24:b::2::2:l:3:f:10\ncell:A25:b::2:::l:3:f:10\ncell:B25:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C25:b::1::1:f:2:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:2:ntvf:1\ncell:G25:b::2::2:l:3:f:10\ncell:A26:b::2:::l:3:f:10\ncell:B26:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C26:b::1::1:f:2:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:2:ntvf:1\ncell:G26:b::2::2:l:3:f:10\ncell:A27:b::2:::l:3:f:10\ncell:B27:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C27:b::1::1:f:2:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:2:ntvf:1\ncell:G27:b::2::2:l:3:f:10\ncell:A28:b::2:::l:3:f:10\ncell:B28:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C28:b::1::1:f:2:cf:2:colspan:3\ncell:D28:b::1:::colspan:2\ncell:F28:b::1:::f:2:ntvf:1\ncell:G28:b::2::2:l:3:f:10\ncell:A29:b::2:::l:3:f:10\ncell:B29:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C29:b::1::1:f:2:cf:2:colspan:3\ncell:D29:b::1:::colspan:2\ncell:F29:b::1:::f:2:ntvf:1\ncell:G29:b::2::2:l:3:f:10\ncell:A30:b::2:::l:3:f:10\ncell:B30:b::2::2:l:3:f:10:cf:2\ncell:C30:b::1::1:f:2:cf:2:colspan:3\ncell:D30:b::1:::colspan:2\ncell:F30:b::1:::f:2:ntvf:1\ncell:G30:b::2::2:l:3:f:10\ncell:A31:b::2:::l:3:f:10\ncell:B31:b::2::2:l:3:f:10:cf:2\ncell:C31:b::1::1:f:2:cf:2:colspan:3\ncell:D31:b::1:::colspan:2\ncell:F31:b::1:::f:2:ntvf:1\ncell:G31:b::2::2:l:3:f:10\ncell:A32:b::2:::l:3:f:10\ncell:B32:b::2::2:l:3:f:10:cf:2\ncell:C32:b::1::1:f:2:cf:2:colspan:3\ncell:D32:b::1:::colspan:2\ncell:F32:b::1:::f:2:ntvf:1\ncell:G32:b::2::2:l:3:f:10\ncell:A33:b::2:::l:3:f:10\ncell:B33:b::2::2:l:3:f:10:cf:2\ncell:C33:b::1::1:f:2:cf:2:colspan:3\ncell:D33:b::1:::colspan:2\ncell:F33:b::1:::f:2:ntvf:1\ncell:G33:b::2::2:l:3:f:10\ncell:A34:b::2:::l:3:f:10\ncell:B34:b::2::2:l:3:f:10:cf:2\ncell:C34:b::1::1:f:2:cf:2:colspan:3\ncell:D34:b::1:::colspan:2\ncell:F34:b::1:::f:2:ntvf:1\ncell:G34:b::2::2:l:3:f:10\ncell:A35:b::2:::l:3:f:7\ncell:B35:b::2::2:l:3:f:10:cf:2\ncell:C35:b::1:1:1:f:2:cf:2:colspan:3\ncell:D35:b::1:1::l:3:f:10:cf:2:colspan:2\ncell:E35:b:::2::l:3:f:10:cf:2\ncell:F35:b::1:1::f:2:ntvf:1\ncell:G35:b::2::2:l:3:f:10\ncell:A36:b::2:::l:3:f:6\ncell:B36:b::2::2:l:3:f:6\ncell:C36:t:TOTAL:b:1:1:1:1:f:11:cf:2:colspan:3\ncell:D36:b:2::2::l:3:f:12:cf:2\ncell:E36:b:2:2:2::l:3:f:12:cf:2\ncell:F36:vtf:n:0:SUM(F23\\cF35):b:1:1:1::f:11:ntvf:1\ncell:G36:b::2::2:l:3:f:7\ncell:A37:b::2:::l:3:f:10\ncell:B37:b::::2:l:3:f:6:cf:2\ncell:C37:b:2::::l:1:f:10\ncell:D37:b:2::::l:1:f:10\ncell:E37:b:2::::l:1:f:10\ncell:F37:b:2::::l:1:f:10\ncell:G37:b::2:::l:3:f:6\ncell:A38:b::2:::l:3:f:10\ncell:B38:b::::2:l:3:f:10\ncell:D38:colspan:3:rowspan:3\ncell:G38:b::2:::l:3:f:10\ncell:A39:b::2:::l:3:f:10\ncell:B39:b::::2:l:3:f:10:cf:2\ncell:C39:f:4:cf:1\ncell:E39:f:3\ncell:F39:t::l:2:f:10\ncell:G39:b::2:::l:3:f:10\ncell:A40:b::2:::l:3:f:10\ncell:B40:b::::2:l:3:f:10\ncell:G40:b::2:::l:3:f:10\ncell:A41:b::2:::l:3:f:10\ncell:B41:b:::2:2:l:3:f:10\ncell:C41:b:::2::l:3:f:10\ncell:D41:b:::2::l:3:f:10\ncell:E41:b:::2::l:3:f:10\ncell:F41:b:::2::l:3:f:10\ncell:G41:b::2:2::l:3:f:10\ncell:B42:b:1:::\ncell:C42:b:1:::\ncell:D42:b:1:::\ncell:E42:b:1:::\ncell:F42:b:1:::\ncell:G42:b:1:::\ncol:A:w:10\ncol:B:w:10\ncol:C:w:62\ncol:D:w:73\ncol:E:w:15\ncol:F:w:68\ncol:G:w:10\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:15.75\nrow:36:h:15.75\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:41:h:14.25\nsheet:c:7:r:42:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:* 9pt Trebuchet MS\nfont:3:italic bold * Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Arial\nfont:7:normal bold 12pt Arial\nfont:8:normal normal * *\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 11pt Trebuchet MS\nfont:12:normal normal 12pt Arial\nfont:13:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nlayout:4:padding:* * * 4px;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", }, - name: "inv3", + name: "inv1", hidden: "0", }, - sheet2: { - sheetstr: { - savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Qty.:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Price:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + }, + EditableCells: { + allow: true, + cells: { + "inv1!B2": true, + "inv1!C5": true, + "inv1!C6": true, + "inv1!C7": true, + "inv1!C8": true, + "inv1!C9": true, + "inv1!C11": true, + "inv1!C12": true, + "inv1!C13": true, + "inv1!C14": true, + "inv1!C15": true, + "inv1!C16": true, + "inv1!C18": true, + "inv1!C20": true, + "inv1!D20": true, + "inv1!C23": true, + "inv1!C24": true, + "inv1!C25": true, + "inv1!C26": true, + "inv1!C27": true, + "inv1!C28": true, + "inv1!C29": true, + "inv1!C30": true, + "inv1!C31": true, + "inv1!C32": true, + "inv1!C33": true, + "inv1!C34": true, + "inv1!C35": true, + "inv1!F23": true, + "inv1!F24": true, + "inv1!F25": true, + "inv1!F26": true, + "inv1!F27": true, + "inv1!F28": true, + "inv1!F29": true, + "inv1!F30": true, + "inv1!F31": true, + "inv1!F32": true, + "inv1!F33": true, + "inv1!F34": true, + "inv1!F35": true, + "inv1!D8": true, + "inv1!D15": true, + "inv1!D18": true, + }, + constraints: { + "inv1!C5": ["prompttext", "0", "1e10", "Name"], + "inv1!C6": ["prompttext", "0", "1e10", "Street Address"], + "inv1!C7": ["prompttext", "0", "1e10", "City, State, Zip"], + "inv1!C8": ["prompttext", "0", "1e10", "Phone"], + "inv1!C9": ["promptemail", "0", "1e10", "Email"], + "inv1!C11": ["prompttext", "0", "1e10", "From"], + "inv1!C12": ["prompttext", "0", "1e10", "Name"], + "inv1!C13": ["prompttext", "0", "1e10", "Street Address"], + "inv1!C14": ["prompttext", "0", "1e10", "City, State, Zip"], + "inv1!C15": ["prompttext", "0", "1e10", "Phone"], + "inv1!C16": ["promptemail", "0", "1e10", "Email"], + "inv1!C18": ["prompttext", "0", "1e10", "Invoice #"], + "inv1!C20": ["prompttext", "0", "1e10", "Date"], + "inv1!D20": ["prompttext", "0", "1e10", "Date"], + "inv1!C23": ["prompttext", "0", "1e10", "Description"], + "inv1!C24": ["prompttext", "0", "1e10", "Description"], + "inv1!C25": ["prompttext", "0", "1e10", "Description"], + "inv1!C26": ["prompttext", "0", "1e10", "Description"], + "inv1!C27": ["prompttext", "0", "1e10", "Description"], + "inv1!C28": ["prompttext", "0", "1e10", "Description"], + "inv1!C29": ["prompttext", "0", "1e10", "Description"], + "inv1!C30": ["prompttext", "0", "1e10", "Description"], + "inv1!C31": ["prompttext", "0", "1e10", "Description"], + "inv1!C32": ["prompttext", "0", "1e10", "Description"], + "inv1!C33": ["prompttext", "0", "1e10", "Description"], + "inv1!C34": ["prompttext", "0", "1e10", "Description"], + "inv1!C35": ["prompttext", "0", "1e10", "Description"], + "inv1!F23": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F24": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F25": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F26": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F27": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F28": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F29": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F30": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F31": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F32": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F33": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F34": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!F35": ["promptdecimal", "0", "1e10", "Amount"], + "inv1!D8": ["prompttext", "0", "1e10", "Phone"], + "inv1!D15": ["prompttext", "0", "1e10", "Phone"], + "inv1!D18": ["promptnumeric", "0", "1e10", "Invoice#"], + }, + }, + }, + }, + + 1002: { + template: "Mobile-Tax-Invoice", + templateId: 1002, + footers: [{ name: "Invoice", index: 1, isActive: true }], + logoCell: { + 1: "F7", + }, + signatureCell: { + 1: "E41", + }, + cellMappings: { + 1: { + Heading: "B2", + Items: { + Name: "Items", + Rows: { + start: 23, + end: 35, + }, + Columns: { + Description: "C", + Amount: "F", }, - name: "sheet7", - hidden: "0", }, - sheet3: { + + Date: "G4", + InvoiceNumber: "B5", + From: { + CompanyName: "B8", + StreetAddress: "B9", + CityStateZip: "B10", + Phone: "B11", + Email: "B12", + }, + BillTo: { + Name: "B15", + CompanyName: "B16", + StreetAddress: "B17", + CityStateZip: "B18", + Phone: "B19", + Email: "B20", + }, + TaxPercentage: "G37", + OtherCharges: "G39", + Notes: { + 1: "B38", + 2: "B39", + 3: "B40", + }, + }, + }, + + msc: { + numsheets: 1, + currentid: "sheet2", + currentname: "inv2", + sheetArr: { + sheet2: { sheetstr: { savestr: - 'version:1.5\ncell:B2:t:INVOICE:l:1:f:12:cf:1:colspan:6\ncell:C2:t::l:2:f:10\ncell:D2:t::l:2:f:10\ncell:E2:l:1:f:8:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:10\ncell:B3:f:5:cf:2:colspan:4\ncell:C3:t::l:2:f:10\ncell:D3:t::l:2:f:10\ncell:F3:l:1:f:6:cf:2\ncell:G3:l:1:f:11:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:11:cf:2\ncell:G4:vtf:nd:45892:TODAY():l:1:f:11:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:9:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:6:cf:2:colspan:2\ncell:G5:l:1:f:11:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:6:cf:2:colspan:2\ncell:G6:l:1:f:11:cf:2\ncell:B7:t:FROM\\c:f:11\ncell:F7:l:1:f:6:cf:2:colspan:2\ncell:G7:l:1:f:11:cf:2\ncell:B8:t:[Company Name]:f:9:colspan:4\ncell:F8:l:1:f:6:cf:2:tvf:4:colspan:2:rowspan:5\ncell:G8:l:1:f:11:cf:2\ncell:B9:t:[Street Address]:f:9:cf:2:colspan:4\ncell:F9:l:1:f:6\ncell:G9:l:1:f:11:cf:1\ncell:B10:t:[City, State, Zip]:f:9:cf:2:colspan:4\ncell:G10:l:1:f:10\ncell:B11:t:Phone\\c :f:9:cf:2:colspan:4\ncell:B12:t:Email\\c:f:9:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:9:cf:2\ncell:B15:t:[Name]:f:9:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:9:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:9:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:9:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:9:cf:2:colspan:6\ncell:B20:t:Email\\c:f:9:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:11:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:10\ncell:D22:t::l:2:f:10\ncell:E22:t::l:2:f:10\ncell:F22:t::l:2:f:10\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:11:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), sheet7!B6, inv3!B6)):b:1:1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:3\ncell:D23:t::l:2:f:3\ncell:E23:t::l:2:f:3\ncell:F23:t::b::2:::l:1:f:3\ncell:G23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), (sheet7!E6*sheet7!F6), (inv3!E6*inv3!F6))):b::1::1:f:3:ntvf:1\ncell:A24:b::1::\ncell:B24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), SHEET7!B7, INV3!B7)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:3\ncell:D24:t::l:2:f:3\ncell:E24:t::l:2:f:3\ncell:F24:t::b::2:::l:1:f:3\ncell:G24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), (SHEET7!E7*SHEET7!F7), (INV3!E7*INV3!F7))):b::1::1:f:3:ntvf:1\ncell:A25:b::1::\ncell:B25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), SHEET7!B8, INV3!B8)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:3\ncell:D25:t::l:2:f:3\ncell:E25:t::l:2:f:3\ncell:F25:t::b::2:::l:1:f:3\ncell:G25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), (SHEET7!E8*SHEET7!F8), (INV3!E8*INV3!F8))):b::1::1:f:3:ntvf:1\ncell:A26:b::1::\ncell:B26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), SHEET7!B9, INV3!B9)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:3\ncell:D26:t::l:2:f:3\ncell:E26:t::l:2:f:3\ncell:F26:t::b::2:::l:1:f:3\ncell:G26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), (SHEET7!E9*SHEET7!F9), (INV3!E9*INV3!F9))):b::1::1:f:3:ntvf:1\ncell:A27:b::1::\ncell:B27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), SHEET7!B10, INV3!B10)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:3\ncell:D27:t::l:2:f:3\ncell:E27:t::l:2:f:3\ncell:F27:t::b::2:::l:1:f:3\ncell:G27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), (SHEET7!E10*SHEET7!F10), (INV3!E10*INV3!F10))):b::1::1:f:3:ntvf:1\ncell:A28:b::1::\ncell:B28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), SHEET7!B11, INV3!B11)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:3\ncell:D28:t::l:2:f:3\ncell:E28:t::l:2:f:3\ncell:F28:t::b::2:::l:1:f:3\ncell:G28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), (SHEET7!E11*SHEET7!F11), (INV3!E11*INV3!F11))):b::1::1:f:3:ntvf:1\ncell:A29:b::1::\ncell:B29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), SHEET7!B12, INV3!B12)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:3\ncell:D29:t::l:2:f:3\ncell:E29:t::l:2:f:3\ncell:F29:t::b::2:::l:1:f:3\ncell:G29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), (SHEET7!E12*SHEET7!F12), (INV3!E12*INV3!F12))):b::1::1:f:3:ntvf:1\ncell:A30:b::1::\ncell:B30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), SHEET7!B13, INV3!B13)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:3\ncell:D30:t::l:2:f:3\ncell:E30:t::l:2:f:3\ncell:F30:t::b::2:::l:1:f:3\ncell:G30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), (SHEET7!E13*SHEET7!F13), (INV3!E13*INV3!F13))):b::1::1:f:3:ntvf:1\ncell:A31:b::1::\ncell:B31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), SHEET7!B14, INV3!B14)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:3\ncell:D31:t::l:2:f:3\ncell:E31:t::l:2:f:3\ncell:F31:t::b::2:::l:1:f:3\ncell:G31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), (SHEET7!E14*SHEET7!F14), (INV3!E14*INV3!F14))):b::1::1:f:3:ntvf:1\ncell:A32:b::1::\ncell:B32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), SHEET7!B15, INV3!B15)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:3\ncell:D32:t::l:2:f:3\ncell:E32:t::l:2:f:3\ncell:F32:t::b::2:::l:1:f:3\ncell:G32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), (SHEET7!E15*SHEET7!F15), (INV3!E15*INV3!F15))):b::1::1:f:3:ntvf:1\ncell:A33:b::1::\ncell:B33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), SHEET7!B16, INV3!B16)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C33:l:2:f:3\ncell:D33:l:2:f:3\ncell:E33:l:2:f:3\ncell:F33:b::2:::l:1:f:3\ncell:G33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), (SHEET7!E16*SHEET7!F16), (INV3!E16*INV3!F16))):b::1::1:f:3:ntvf:1\ncell:A34:b::1::\ncell:B34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), SHEET7!B17, INV3!B17)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C34:t::l:2:f:3\ncell:D34:t::l:2:f:3\ncell:E34:t::l:2:f:3\ncell:F34:t::b::2:::l:1:f:3\ncell:G34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), (SHEET7!E17*SHEET7!F17), (INV3!E17*INV3!F17))):b::1::1:f:3:ntvf:1\ncell:A35:b::1::\ncell:B35:vtf:t: :IF(AND(ISBLANK(INV3!B18), ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), SHEET7!B18, INV3!B18)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:3\ncell:D35:t::b:::2::l:1:f:3\ncell:E35:t::b:::2::l:1:f:3\ncell:F35:t::b::2:2::l:1:f:3\ncell:G35:vtf:t: :IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), (SHEET7!E18*SHEET7!F18), (INV3!E18*INV3!F18))):b::1::1:f:3:ntvf:1\ncell:B36:b:2::::l:1:f:10\ncell:C36:b:2::::l:1:f:10\ncell:D36:b:2::::l:1:f:10\ncell:E36:b:2::::l:1:f:11\ncell:F36:t:Subtotal:b:2::::l:1:f:9\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:9:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:11:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:10\ncell:D37:t::b:::2::l:1:f:10\ncell:F37:t:Tax Rate:l:1:f:9\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:10\ncell:D38:t::b:2::::l:1:f:10\ncell:F38:t:Tax:l:1:f:9\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:10\ncell:D39:t::l:2:f:10\ncell:F39:t:Other:b:::1::l:1:f:9\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:10\ncell:D40:t::l:2:f:10\ncell:F40:t:TOTAL:b:1::::l:1:f:11\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:9:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:4:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:10\ncell:D43:t::l:2:f:10\ncell:E43:t::l:2:f:10\ncell:F43:t::l:2:f:10\ncell:G43:t::l:2:f:10\ncol:A:w:10\ncol:B:w:65\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:53\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 9pt Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:italic normal * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 14pt Trebuchet MS\nfont:8:normal bold 28pt Trebuchet MS\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 10pt Trebuchet MS\nfont:12:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', + "version:1.5\ncell:B2:t:INVOICE:l:1:f:9:cf:1:colspan:6\ncell:C2:t::l:2:f:13\ncell:D2:t::l:2:f:13\ncell:E2:l:1:f:10:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:8:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:13\ncell:B3:f:6:cf:2:colspan:4\ncell:C3:t::l:2:f:13\ncell:D3:t::l:2:f:13\ncell:F3:l:1:f:7:cf:2\ncell:G3:l:1:f:14:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:14:cf:2\ncell:G4:vtf:nd:45898:TODAY():l:1:f:14:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:3:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:7:cf:2:colspan:2\ncell:G5:l:1:f:14:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:7:cf:2:colspan:2\ncell:G6:l:1:f:14:cf:2\ncell:B7:t:FROM\\c:f:12\ncell:F7:l:1:f:7:cf:2:tvf:4:colspan:2:rowspan:6\ncell:G7:l:1:f:14:cf:2\ncell:B8:t:[Company Name]:f:3:colspan:4\ncell:F8:l:1:f:7:cf:2:colspan:2\ncell:G8:l:1:f:14:cf:2\ncell:B9:t:[Street Address]:f:1:cf:2:colspan:4\ncell:F9:l:1:f:7\ncell:G9:l:1:f:14:cf:1\ncell:B10:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:G10:l:1:f:13\ncell:B11:t:Phone\\c :f:1:cf:2:colspan:4\ncell:B12:t:Email\\c:f:1:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:11:cf:2\ncell:B15:t:[Name]:f:1:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:1:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:1:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:1:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:1:cf:2:colspan:6\ncell:B20:t:Email\\c:f:1:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:14:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:13\ncell:D22:t::l:2:f:13\ncell:E22:t::l:2:f:13\ncell:F22:t::l:2:f:13\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:14:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:b:1:1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:4\ncell:D23:t::l:2:f:4\ncell:E23:t::l:2:f:4\ncell:F23:t::b::2:::l:1:f:4\ncell:G23:b::1::1:f:4:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:4\ncell:D24:t::l:2:f:4\ncell:E24:t::l:2:f:4\ncell:F24:t::b::2:::l:1:f:4\ncell:G24:b::1::1:f:4:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:4\ncell:D25:t::l:2:f:4\ncell:E25:t::l:2:f:4\ncell:F25:t::b::2:::l:1:f:4\ncell:G25:b::1::1:f:4:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:4\ncell:D26:t::l:2:f:4\ncell:E26:t::l:2:f:4\ncell:F26:t::b::2:::l:1:f:4\ncell:G26:b::1::1:f:4:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:4\ncell:D27:t::l:2:f:4\ncell:E27:t::l:2:f:4\ncell:F27:t::b::2:::l:1:f:4\ncell:G27:b::1::1:f:4:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:4\ncell:D28:t::l:2:f:4\ncell:E28:t::l:2:f:4\ncell:F28:t::b::2:::l:1:f:4\ncell:G28:b::1::1:f:4:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:4\ncell:D29:t::l:2:f:4\ncell:E29:t::l:2:f:4\ncell:F29:t::b::2:::l:1:f:4\ncell:G29:b::1::1:f:4:ntvf:1\ncell:A30:b::1::\ncell:B30:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:4\ncell:D30:t::l:2:f:4\ncell:E30:t::l:2:f:4\ncell:F30:t::b::2:::l:1:f:4\ncell:G30:b::1::1:f:4:ntvf:1\ncell:A31:b::1::\ncell:B31:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:4\ncell:D31:t::l:2:f:4\ncell:E31:t::l:2:f:4\ncell:F31:t::b::2:::l:1:f:4\ncell:G31:b::1::1:f:4:ntvf:1\ncell:A32:b::1::\ncell:B32:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:4\ncell:D32:t::l:2:f:4\ncell:E32:t::l:2:f:4\ncell:F32:t::b::2:::l:1:f:4\ncell:G32:b::1::1:f:4:ntvf:1\ncell:A33:b::1::\ncell:B33:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C33:t::l:2:f:4\ncell:D33:t::l:2:f:4\ncell:E33:t::l:2:f:4\ncell:F33:t::b::2:::l:1:f:4\ncell:G33:b::1::1:f:4:ntvf:1\ncell:A34:b::1::\ncell:B34:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C34:l:2:f:4\ncell:D34:l:2:f:4\ncell:E34:l:2:f:4\ncell:F34:b::2:::l:1:f:4\ncell:G34:b::1::1:f:4:ntvf:1\ncell:A35:b::1::\ncell:B35:b::1:1:1:f:4:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:4\ncell:D35:t::b:::2::l:1:f:4\ncell:E35:t::b:::2::l:1:f:4\ncell:F35:t::b::2:2::l:1:f:4\ncell:G35:b::1:1:1:f:4:ntvf:1\ncell:B36:b:2::::l:1:f:13\ncell:C36:b:2::::l:1:f:13\ncell:D36:b:2::::l:1:f:13\ncell:E36:b:2::::l:1:f:14\ncell:F36:t:Subtotal:b:2::::l:1:f:11\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:11:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:14:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:13\ncell:D37:t::b:::2::l:1:f:13\ncell:F37:t:Tax Rate:l:1:f:11\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:13\ncell:D38:t::b:2::::l:1:f:13\ncell:F38:t:Tax:l:1:f:11\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:13\ncell:D39:t::l:2:f:13\ncell:F39:t:Other:b:::1::l:1:f:11\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:13\ncell:D40:t::l:2:f:13\ncell:F40:t:TOTAL:b:1::::l:1:f:14\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:11:ntvf:1\ncell:E41:colspan:3:rowspan:3\ncell:E42:colspan:3:rowspan:2\ncell:B43:l:1:f:5:cf:1\ncell:C43:t::l:2:f:13\ncell:D43:t::l:2:f:13\ncell:E43:t::l:2:f:13\ncell:F43:t::l:2:f:13\ncell:G43:t::l:2:f:13\ncol:A:w:10\ncol:B:w:90\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:45\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 10pt Trebuchet MS\nfont:4:* 9pt Trebuchet MS\nfont:5:italic bold 10pt Trebuchet MS\nfont:6:italic normal * Trebuchet MS\nfont:7:normal bold 10pt Trebuchet MS\nfont:8:normal bold 14pt Trebuchet MS\nfont:9:normal bold 16pt Trebuchet MS\nfont:10:normal bold 28pt Trebuchet MS\nfont:11:normal normal * Trebuchet MS\nfont:12:normal normal 10pt *\nfont:13:normal normal 10pt Arial\nfont:14:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", }, - name: "sheet6", + name: "inv2", hidden: "0", }, }, EditableCells: { allow: true, cells: { - "sheet6!B2": true, - "sheet6!F4": true, - "sheet6!G4": true, - "sheet6!B5": true, - "sheet6!B7": true, - "sheet6!B8": true, - "sheet6!B9": true, - "sheet6!B10": true, - "sheet6!B11": true, - "sheet6!B12": true, - "sheet6!B14": true, - "sheet6!B15": true, - "sheet6!B16": true, - "sheet6!B17": true, - "sheet6!B18": true, - "sheet6!B19": true, - "sheet6!B20": true, - "sheet6!B38": true, - "sheet6!B39": true, - "sheet6!B40": true, - "sheet6!F36": true, - "sheet6!G37": true, - "sheet6!F37": true, - "sheet6!F39": true, - "sheet6!G39": true, - "sheet6!F38": true, - "inv3!B2": true, - "inv3!B6": true, - "inv3!B7": true, - "inv3!B8": true, - "inv3!B9": true, - "inv3!B10": true, - "inv3!B11": true, - "inv3!B12": true, - "inv3!B13": true, - "inv3!B14": true, - "inv3!B15": true, - "inv3!B16": true, - "inv3!B17": true, - "inv3!B18": true, - "inv3!E6": true, - "inv3!E7": true, - "inv3!E8": true, - "inv3!E9": true, - "inv3!E10": true, - "inv3!E11": true, - "inv3!E12": true, - "inv3!E13": true, - "inv3!E14": true, - "inv3!E15": true, - "inv3!E16": true, - "inv3!E17": true, - "inv3!E18": true, - "inv3!F6": true, - "inv3!F7": true, - "inv3!F8": true, - "inv3!F9": true, - "inv3!F10": true, - "inv3!F11": true, - "inv3!F12": true, - "inv3!F13": true, - "inv3!F14": true, - "inv3!F15": true, - "inv3!F16": true, - "inv3!F17": true, - "inv3!F18": true, - "sheet7!F6": true, - "sheet7!F7": true, - "sheet7!F8": true, - "sheet7!F9": true, - "sheet7!F10": true, - "sheet7!F11": true, - "sheet7!F12": true, - "sheet7!F13": true, - "sheet7!F14": true, - "sheet7!F15": true, - "sheet7!F16": true, - "sheet7!F17": true, - "sheet7!F18": true, - "sheet7!E6": true, - "sheet7!E7": true, - "sheet7!E8": true, - "sheet7!E9": true, - "sheet7!E10": true, - "sheet7!E11": true, - "sheet7!E12": true, - "sheet7!E13": true, - "sheet7!E14": true, - "sheet7!E15": true, - "sheet7!E16": true, - "sheet7!E17": true, - "sheet7!E18": true, - "sheet7!B6": true, - "sheet7!B2": true, - "sheet7!B7": true, - "sheet7!B8": true, - "sheet7!B9": true, - "sheet7!B10": true, - "sheet7!B11": true, - "sheet7!B12": true, - "sheet7!B13": true, - "sheet7!B14": true, - "sheet7!B15": true, - "sheet7!B16": true, - "sheet7!B17": true, - "sheet7!B18": true, - "sheet6!C5": true, - }, - constraints: { - "sheet6!B2": ["prompttext", "0", "1e10", "Invoice"], - "sheet6!F4": ["prompttext", "0", "1e10", "Date"], - "sheet6!G4": ["prompttext", "0", "1e10", "Date"], - "sheet6!B5": ["prompttext", "0", "1e10", "Invoice #"], - "sheet6!B7": ["prompttext", "0", "1e10", "From"], - "sheet6!B8": ["prompttext", "0", "1e10", "Company Name"], - "sheet6!B9": ["prompttext", "0", "1e10", "Street Address"], - "sheet6!B10": ["prompttext", "0", "1e10", "City, State, Zip"], - "sheet6!B11": ["prompttext", "0", "1e10", "Phone"], - "sheet6!B12": ["promptemail", "0", "1e10", "Email"], - "sheet6!B14": ["prompttext", "0", "1e10", "Bill To"], - "sheet6!B15": ["prompttext", "0", "1e10", "Name"], - "sheet6!B16": ["prompttext", "0", "1e10", "Company Name"], - "sheet6!B17": ["prompttext", "0", "1e10", "Street Address"], - "sheet6!B18": ["prompttext", "0", "1e10", "City, State, Zip"], - "sheet6!B19": ["prompttext", "0", "1e10", "Phone"], - "sheet6!B20": ["promptemail", "0", "1e10", "Email"], - "sheet6!B38": ["prompttext", "0", "1e10", "Notes"], - "sheet6!B39": ["prompttext", "0", "1e10", "Notes"], - "sheet6!B40": ["prompttext", "0", "1e10", "Notes"], - "sheet6!F36": ["prompttext", "0", "1e10", "Subtotal"], - "sheet6!G37": ["promptdecimal", "0", "1e10", "Tax Rate (0.00)"], - "sheet6!F37": ["prompttext", "0", "1e10", "Tax Rate"], - "sheet6!F39": ["prompttext", "0", "1e10", "Other"], - "sheet6!G39": ["promptdecimal", "0", "1e10", "Other"], - "sheet6!F38": ["prompttext", "0", "1e10", "Tax"], - "inv3!B6": ["prompttext", "0", "1e10", "Description"], - "inv3!B7": ["prompttext", "0", "1e10", "Description"], - "inv3!B8": ["prompttext", "0", "1e10", "Description"], - "inv3!B9": ["prompttext", "0", "1e10", "Description"], - "inv3!B10": ["prompttext", "0", "1e10", "Description"], - "inv3!B11": ["prompttext", "0", "1e10", "Description"], - "inv3!B12": ["prompttext", "0", "1e10", "Description"], - "inv3!B13": ["prompttext", "0", "1e10", "Description"], - "inv3!B14": ["prompttext", "0", "1e10", "Description"], - "inv3!B15": ["prompttext", "0", "1e10", "Description"], - "inv3!B16": ["prompttext", "0", "1e10", "Description"], - "inv3!B17": ["prompttext", "0", "1e10", "Description"], - "inv3!B18": ["prompttext", "0", "1e10", "Description"], - "sheet7!B6": ["prompttext", "0", "1e10", "Description"], - "sheet7!B7": ["prompttext", "0", "1e10", "Description"], - "sheet7!B8": ["prompttext", "0", "1e10", "Description"], - "sheet7!B9": ["prompttext", "0", "1e10", "Description"], - "sheet7!B10": ["prompttext", "0", "1e10", "Description"], - "sheet7!B11": ["prompttext", "0", "1e10", "Description"], - "sheet7!B12": ["prompttext", "0", "1e10", "Description"], - "sheet7!B13": ["prompttext", "0", "1e10", "Description"], - "sheet7!B14": ["prompttext", "0", "1e10", "Description"], - "sheet7!B15": ["prompttext", "0", "1e10", "Description"], - "sheet7!B16": ["prompttext", "0", "1e10", "Description"], - "sheet7!B17": ["prompttext", "0", "1e10", "Description"], - "sheet7!B18": ["prompttext", "0", "1e10", "Description"], - "sheet6!C5": ["promptnumeric", "0", "1e10", "Invoice#"], - "sheet7!E6": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E7": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E8": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E9": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E10": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E11": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E12": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E13": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E14": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E15": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E16": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E17": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!E18": ["promptdecimal", "0", "1e10", "Quantity"], - "sheet7!F6": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F7": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F8": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F9": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F10": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F11": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F12": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F13": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F14": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F15": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F16": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F17": ["promptdecimal", "0", "1e10", "Price"], - "sheet7!F18": ["promptdecimal", "0", "1e10", "Price"], - "inv3!E6": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E7": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E8": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E9": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E10": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E11": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E12": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E13": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E14": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E15": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E16": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E17": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!E18": ["promptdecimal", "0", "1e10", "Hours"], - "inv3!F6": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F7": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F8": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F9": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F10": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F11": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F12": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F13": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F14": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F15": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F16": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F17": ["promptdecimal", "0", "1e10", "Rate"], - "inv3!F18": ["promptdecimal", "0", "1e10", "Rate"], - }, - }, - }, - - footers: [ - { name: "Detail1", index: 1, isActive: true }, - { name: "Detail2", index: 2, isActive: false }, - { name: "Invoice", index: 3, isActive: false }, - ], - logoCell: { - 1: "", - 2: "", - 3: "F8", - }, - signatureCell: { - 1: "", - 2: "", - 3: "", - }, - cellMappings: { - 1: { - Heading: "B2", - Items: { - Range: { - start: 6, - end: 18, - }, - Content: { - Description: "B", - Hours: "E", - Rate: "F", - }, - }, - }, - 2: { - Heading: "B2", - Items: { - name: "Items", - Range: { - start: 6, - end: 18, - }, - Content: { - Description: "B", - Qty: "E", - Price: "F", - }, - }, - }, - 3: { - Heading: "B2", - Date: "G4", - InvoiceNumber: "B5", - From: { - CompanyName: "B8", - StreetAddress: "B9", - CityStateZip: "B10", - Phone: "B11", - Email: "B12", - }, - BillTo: { - Name: "B15", - CompanyName: "B16", - StreetAddress: "B17", - CityStateZip: "B18", - Phone: "B19", - Email: "B20", - }, - TaxPercentage: "G37", - OtherCharges: "G39", - Notes: { - 1: "B38", - 2: "B39", - 3: "B40", - }, - }, - }, - }, - 2: { - template: "Mobile Invoice 2", - templateId: 2, - msc: { - numsheets: 2, - currentid: "sheet1", - currentname: "inv1", - sheetArr: { - sheet1: { - sheetstr: { - savestr: - "version:1.5\ncell:B1:b:::2::l:1:f:10\ncell:C1:b:::2::l:1:f:10\ncell:D1:b:::2::l:1:f:10\ncell:E1:b:::2::l:1:f:10\ncell:F1:b:::2::l:1:f:10\ncell:G1:b:::2::l:1:f:10\ncell:A2:b::2:::l:1:f:10\ncell:B2:t:INVOICE:b:1:1:1:1:f:13:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:10\ncell:D2:t::b:2::2::l:1:f:10\ncell:E2:t::b:2::2::l:1:f:10\ncell:F2:t::b:2::2::l:1:f:10\ncell:G2:t::b:2::2::l:1:f:10\ncell:A3:b::2:::l:3:f:10\ncell:B3:b:2:::2:l:3:f:10\ncell:C3:b:2::::l:1:f:10\ncell:D3:b:2::::l:1:f:10\ncell:E3:b:2::::l:1:f:10\ncell:F3:b:2::::l:1:f:10\ncell:G3:b:2:2:::l:3:f:10\ncell:A4:b::2:::l:3:f:10\ncell:B4:b::::2:l:3:f:6\ncell:C4:t:BILL TO\\c:f:9:colspan:2\ncell:E4:f:5:cf:2\ncell:G4:b::2:::l:3:f:10\ncell:A5:b::2:::l:3:f:10\ncell:B5:b::::2:l:3:f:7\ncell:C5:t:[Name]:f:9:cf:2:colspan:3\ncell:E5:f:1:cf:2\ncell:F5:t::l:2:f:10:tvf:4:rowspan:6\ncell:G5:t::b::1:::l:2:f:10\ncell:A6:b::2:::l:3:f:10\ncell:B6:b::::2:l:3:f:7\ncell:C6:t:[Street Address]:f:9:cf:2:colspan:3\ncell:E6:f:1:cf:2\ncell:F6:l:2:f:10\ncell:G6:b::1:::l:2:f:10\ncell:A7:b::2:::l:3:f:10\ncell:B7:b::::2:l:3:f:7\ncell:C7:t:[City, State, Zip]:f:9:cf:2:colspan:3\ncell:E7:f:1:cf:2\ncell:G7:b::2:::l:3:f:10\ncell:A8:b::2:::l:3:f:10\ncell:B8:b::::2:l:3:f:7\ncell:C8:t:Phone\\c:f:9:cf:2:colspan:3\ncell:D8:f:8:cf:2\ncell:E8:f:1:cf:2\ncell:G8:b::2:::l:3:f:10\ncell:A9:b::2:::l:3:f:10\ncell:B9:b::::2:l:3:f:7\ncell:C9:t:Email\\c:f:9:cf:2:colspan:3\ncell:E9:f:1:cf:2\ncell:G9:b::2:::l:3:f:10\ncell:A10:b::2:::l:3:f:10\ncell:B10:b::::2:l:3:f:7\ncell:C10:colspan:2\ncell:G10:b::2:::l:3:f:10\ncell:A11:b::2:::l:3:f:10\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:FROM\\c:f:9:colspan:2\ncell:E11:f:8:colspan:2\ncell:G11:b::2:::l:3:f:10\ncell:A12:b::2:::l:3:f:10\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[Name]:f:9:colspan:4\ncell:E12:colspan:2\ncell:G12:b::2:::l:3:f:10\ncell:A13:b::2:::l:3:f:10\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:[Street Address]:f:9:colspan:4\ncell:E13:colspan:2\ncell:G13:b::2:::l:3:f:10\ncell:A14:b::2:::l:3:f:10\ncell:B14:b::::2:l:3:f:7\ncell:C14:t:[City, State, Zip]:f:9:colspan:4\ncell:E14:colspan:2\ncell:G14:b::2:::l:3:f:10\ncell:A15:b::2:::l:3:f:10\ncell:B15:b::::2:l:3:f:7\ncell:C15:t:Phone\\c:f:9:colspan:4\ncell:D15:f:8:cf:2:colspan:3\ncell:E15:colspan:2\ncell:G15:b::2:::l:3:f:10\ncell:A16:b::2:::l:3:f:10\ncell:B16:b::::2:l:3:f:7\ncell:C16:t:Email\\c:f:9:colspan:4\ncell:E16:colspan:2\ncell:G16:b::2:::l:3:f:10\ncell:A17:b::2:::l:3:f:10\ncell:B17:b::::2:l:3:f:6\ncell:G17:b::2:::l:3:f:10\ncell:A18:b::2:::l:3:f:10\ncell:B18:b::::2:l:3:f:10\ncell:C18:t:INVOICE #\\c:f:9:cf:2:colspan:4\ncell:D18:v:1:l:4:f:9:cf:2:colspan:3\ncell:G18:b::2:::l:3:f:10\ncell:A19:b::2:::l:3:f:10\ncell:B19:b::::2:l:3:f:10\ncell:G19:b::2:::l:3:f:10\ncell:A20:b::2:::l:3:f:10\ncell:B20:b::::2:l:3:f:10\ncell:C20:t:DATE\\c:f:9:cf:2\ncell:D20:vtf:nd:45892:TODAY():f:9:cf:2:ntvf:3:colspan:2\ncell:G20:b::2:::l:3:f:10\ncell:A21:b::2:::l:3:f:10\ncell:B21:b::::2:l:3:f:6:cf:2\ncell:C21:b:::2::l:1:f:10\ncell:D21:b:::2::l:3:f:6:cf:2\ncell:E21:b:::2::l:1:f:10\ncell:F21:b:::2::l:1:f:10\ncell:G21:b::2:::l:3:f:10\ncell:A22:b::2:::l:3:f:10\ncell:B22:b::2::2:l:3:f:10\ncell:C22:t:Description:b:1::1:1:f:9:cf:1:colspan:3\ncell:D22:t:Description:b:1::1::l:3:f:6:cf:2:colspan:2:rowspan:1\ncell:E22:t::b:2:2:2::l:1:f:10\ncell:F22:t:Amount:b:1:1:1:1:f:9:cf:1\ncell:G22:b::2::2:l:3:f:10\ncell:A23:b::2:::l:3:f:10\ncell:B23:b::2::2:l:3:f:10\ncell:C23:b:1:1::1:f:2:cf:2:colspan:3\ncell:D23:b:1:1:::l:3:f:10:cf:2:colspan:2:rowspan:1\ncell:E23:t::b:2:2:2::l:1:f:10\ncell:F23:b:1:1:::f:2:ntvf:1\ncell:G23:b::2::2:l:3:f:10\ncell:A24:b::2:::l:3:f:10\ncell:B24:b::2::2:l:3:f:10:cf:2\ncell:C24:b::1::1:f:2:cf:2:colspan:3\ncell:D24:b::1:::l:1:f:10:colspan:2\ncell:E24:l:1:f:10\ncell:F24:b::1:::f:2:ntvf:1\ncell:G24:b::2::2:l:3:f:10\ncell:A25:b::2:::l:3:f:10\ncell:B25:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C25:b::1::1:f:2:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:2:ntvf:1\ncell:G25:b::2::2:l:3:f:10\ncell:A26:b::2:::l:3:f:10\ncell:B26:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C26:b::1::1:f:2:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:2:ntvf:1\ncell:G26:b::2::2:l:3:f:10\ncell:A27:b::2:::l:3:f:10\ncell:B27:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C27:b::1::1:f:2:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:2:ntvf:1\ncell:G27:b::2::2:l:3:f:10\ncell:A28:b::2:::l:3:f:10\ncell:B28:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C28:b::1::1:f:2:cf:2:colspan:3\ncell:D28:b::1:::colspan:2\ncell:F28:b::1:::f:2:ntvf:1\ncell:G28:b::2::2:l:3:f:10\ncell:A29:b::2:::l:3:f:10\ncell:B29:b::2::2:l:3:f:10:cf:2:ntvf:2\ncell:C29:b::1::1:f:2:cf:2:colspan:3\ncell:D29:b::1:::colspan:2\ncell:F29:b::1:::f:2:ntvf:1\ncell:G29:b::2::2:l:3:f:10\ncell:A30:b::2:::l:3:f:10\ncell:B30:b::2::2:l:3:f:10:cf:2\ncell:C30:b::1::1:f:2:cf:2:colspan:3\ncell:D30:b::1:::colspan:2\ncell:F30:b::1:::f:2:ntvf:1\ncell:G30:b::2::2:l:3:f:10\ncell:A31:b::2:::l:3:f:10\ncell:B31:b::2::2:l:3:f:10:cf:2\ncell:C31:b::1::1:f:2:cf:2:colspan:3\ncell:D31:b::1:::colspan:2\ncell:F31:b::1:::f:2:ntvf:1\ncell:G31:b::2::2:l:3:f:10\ncell:A32:b::2:::l:3:f:10\ncell:B32:b::2::2:l:3:f:10:cf:2\ncell:C32:b::1::1:f:2:cf:2:colspan:3\ncell:D32:b::1:::colspan:2\ncell:F32:b::1:::f:2:ntvf:1\ncell:G32:b::2::2:l:3:f:10\ncell:A33:b::2:::l:3:f:10\ncell:B33:b::2::2:l:3:f:10:cf:2\ncell:C33:b::1::1:f:2:cf:2:colspan:3\ncell:D33:b::1:::colspan:2\ncell:F33:b::1:::f:2:ntvf:1\ncell:G33:b::2::2:l:3:f:10\ncell:A34:b::2:::l:3:f:10\ncell:B34:b::2::2:l:3:f:10:cf:2\ncell:C34:b::1::1:f:2:cf:2:colspan:3\ncell:D34:b::1:::colspan:2\ncell:F34:b::1:::f:2:ntvf:1\ncell:G34:b::2::2:l:3:f:10\ncell:A35:b::2:::l:3:f:7\ncell:B35:b::2::2:l:3:f:10:cf:2\ncell:C35:b::1:1:1:f:2:cf:2:colspan:3\ncell:D35:b::1:1::l:3:f:10:cf:2:colspan:2\ncell:E35:b:::2::l:3:f:10:cf:2\ncell:F35:b::1:1::f:2:ntvf:1\ncell:G35:b::2::2:l:3:f:10\ncell:A36:b::2:::l:3:f:6\ncell:B36:b::2::2:l:3:f:6\ncell:C36:t:TOTAL:b:1:1:1:1:f:11:cf:2:colspan:3\ncell:D36:b:2::2::l:3:f:12:cf:2\ncell:E36:b:2:2:2::l:3:f:12:cf:2\ncell:F36:vtf:n:0:SUM(F23\\cF35):b:1:1:1::f:11:ntvf:1\ncell:G36:b::2::2:l:3:f:7\ncell:A37:b::2:::l:3:f:10\ncell:B37:b::::2:l:3:f:6:cf:2\ncell:C37:b:2::::l:1:f:10\ncell:D37:b:2::::l:1:f:10\ncell:E37:b:2::::l:1:f:10\ncell:F37:b:2::::l:1:f:10\ncell:G37:b::2:::l:3:f:6\ncell:A38:b::2:::l:3:f:10\ncell:B38:b::::2:l:3:f:10\ncell:G38:b::2:::l:3:f:10\ncell:A39:b::2:::l:3:f:10\ncell:B39:b::::2:l:3:f:10:cf:2\ncell:C39:t:Thank you for your business:f:4:cf:1:colspan:4\ncell:D39:t:Thank you for your business:colspan:3\ncell:E39:t:Thank you for your business:f:3:colspan:2\ncell:F39:t::l:2:f:10\ncell:G39:b::2:::l:3:f:10\ncell:A40:b::2:::l:3:f:10\ncell:B40:b::::2:l:3:f:10\ncell:G40:b::2:::l:3:f:10\ncell:A41:b::2:::l:3:f:10\ncell:B41:b:::2:2:l:3:f:10\ncell:C41:b:::2::l:3:f:10\ncell:D41:b:::2::l:3:f:10\ncell:E41:b:::2::l:3:f:10\ncell:F41:b:::2::l:3:f:10\ncell:G41:b::2:2::l:3:f:10\ncell:B42:b:1:::\ncell:C42:b:1:::\ncell:D42:b:1:::\ncell:E42:b:1:::\ncell:F42:b:1:::\ncell:G42:b:1:::\ncol:A:w:10\ncol:B:w:10\ncol:C:w:62\ncol:D:w:73\ncol:E:w:15\ncol:F:w:68\ncol:G:w:10\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:15.75\nrow:36:h:15.75\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:41:h:14.25\nsheet:c:7:r:42:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:* 9pt Trebuchet MS\nfont:3:italic bold * Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Arial\nfont:7:normal bold 12pt Arial\nfont:8:normal normal * *\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 11pt Trebuchet MS\nfont:12:normal normal 12pt Arial\nfont:13:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nlayout:4:padding:* * * 4px;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", - }, - name: "inv1", - hidden: "0", - }, - sheet2: { - sheetstr: { - savestr: - "version:1.5\ncell:B2:t:INVOICE:l:1:f:9:cf:1:colspan:6\ncell:C2:t::l:2:f:13\ncell:D2:t::l:2:f:13\ncell:E2:l:1:f:10:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:8:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:13\ncell:B3:f:6:cf:2:colspan:4\ncell:C3:t::l:2:f:13\ncell:D3:t::l:2:f:13\ncell:F3:l:1:f:7:cf:2\ncell:G3:l:1:f:14:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:14:cf:2\ncell:G4:vtf:nd:42081:TODAY():l:1:f:14:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:3:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:7:cf:2:colspan:2\ncell:G5:l:1:f:14:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:7:cf:2:colspan:2\ncell:G6:l:1:f:14:cf:2\ncell:B7:t:FROM\\c:f:12\ncell:F7:l:1:f:7:cf:2:tvf:4:colspan:2:rowspan:6\ncell:G7:l:1:f:14:cf:2\ncell:B8:t:[Company Name]:f:3:colspan:4\ncell:F8:l:1:f:7:cf:2:colspan:2\ncell:G8:l:1:f:14:cf:2\ncell:B9:t:[Street Address]:f:1:cf:2:colspan:4\ncell:F9:l:1:f:7\ncell:G9:l:1:f:14:cf:1\ncell:B10:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:G10:l:1:f:13\ncell:B11:t:Phone\\c :f:1:cf:2:colspan:4\ncell:B12:t:Email\\c:f:1:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:11:cf:2\ncell:B15:t:[Name]:f:1:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:1:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:1:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:1:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:1:cf:2:colspan:6\ncell:B20:t:Email\\c:f:1:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:14:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:13\ncell:D22:t::l:2:f:13\ncell:E22:t::l:2:f:13\ncell:F22:t::l:2:f:13\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:14:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:b:1:1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:4\ncell:D23:t::l:2:f:4\ncell:E23:t::l:2:f:4\ncell:F23:t::b::2:::l:1:f:4\ncell:G23:b::1::1:f:4:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:4\ncell:D24:t::l:2:f:4\ncell:E24:t::l:2:f:4\ncell:F24:t::b::2:::l:1:f:4\ncell:G24:b::1::1:f:4:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:4\ncell:D25:t::l:2:f:4\ncell:E25:t::l:2:f:4\ncell:F25:t::b::2:::l:1:f:4\ncell:G25:b::1::1:f:4:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:4\ncell:D26:t::l:2:f:4\ncell:E26:t::l:2:f:4\ncell:F26:t::b::2:::l:1:f:4\ncell:G26:b::1::1:f:4:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:4\ncell:D27:t::l:2:f:4\ncell:E27:t::l:2:f:4\ncell:F27:t::b::2:::l:1:f:4\ncell:G27:b::1::1:f:4:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:4\ncell:D28:t::l:2:f:4\ncell:E28:t::l:2:f:4\ncell:F28:t::b::2:::l:1:f:4\ncell:G28:b::1::1:f:4:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:4\ncell:D29:t::l:2:f:4\ncell:E29:t::l:2:f:4\ncell:F29:t::b::2:::l:1:f:4\ncell:G29:b::1::1:f:4:ntvf:1\ncell:A30:b::1::\ncell:B30:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:4\ncell:D30:t::l:2:f:4\ncell:E30:t::l:2:f:4\ncell:F30:t::b::2:::l:1:f:4\ncell:G30:b::1::1:f:4:ntvf:1\ncell:A31:b::1::\ncell:B31:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:4\ncell:D31:t::l:2:f:4\ncell:E31:t::l:2:f:4\ncell:F31:t::b::2:::l:1:f:4\ncell:G31:b::1::1:f:4:ntvf:1\ncell:A32:b::1::\ncell:B32:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:4\ncell:D32:t::l:2:f:4\ncell:E32:t::l:2:f:4\ncell:F32:t::b::2:::l:1:f:4\ncell:G32:b::1::1:f:4:ntvf:1\ncell:A33:b::1::\ncell:B33:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C33:t::l:2:f:4\ncell:D33:t::l:2:f:4\ncell:E33:t::l:2:f:4\ncell:F33:t::b::2:::l:1:f:4\ncell:G33:b::1::1:f:4:ntvf:1\ncell:A34:b::1::\ncell:B34:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C34:l:2:f:4\ncell:D34:l:2:f:4\ncell:E34:l:2:f:4\ncell:F34:b::2:::l:1:f:4\ncell:G34:b::1::1:f:4:ntvf:1\ncell:A35:b::1::\ncell:B35:b::1:1:1:f:4:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:4\ncell:D35:t::b:::2::l:1:f:4\ncell:E35:t::b:::2::l:1:f:4\ncell:F35:t::b::2:2::l:1:f:4\ncell:G35:b::1:1:1:f:4:ntvf:1\ncell:B36:b:2::::l:1:f:13\ncell:C36:b:2::::l:1:f:13\ncell:D36:b:2::::l:1:f:13\ncell:E36:b:2::::l:1:f:14\ncell:F36:t:Subtotal:b:2::::l:1:f:11\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:11:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:14:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:13\ncell:D37:t::b:::2::l:1:f:13\ncell:F37:t:Tax Rate:l:1:f:11\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:13\ncell:D38:t::b:2::::l:1:f:13\ncell:F38:t:Tax:l:1:f:11\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:13\ncell:D39:t::l:2:f:13\ncell:F39:t:Other:b:::1::l:1:f:11\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:13\ncell:D40:t::l:2:f:13\ncell:F40:t:TOTAL:b:1::::l:1:f:14\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:11:ntvf:1\ncell:B43:t:Thank you for your business:l:1:f:5:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:13\ncell:D43:t::l:2:f:13\ncell:E43:t::l:2:f:13\ncell:F43:t::l:2:f:13\ncell:G43:t::l:2:f:13\ncol:A:w:10\ncol:B:w:90\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:45\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75:needsrecalc:yes\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 10pt Trebuchet MS\nfont:4:* 9pt Trebuchet MS\nfont:5:italic bold 10pt Trebuchet MS\nfont:6:italic normal * Trebuchet MS\nfont:7:normal bold 10pt Trebuchet MS\nfont:8:normal bold 14pt Trebuchet MS\nfont:9:normal bold 16pt Trebuchet MS\nfont:10:normal bold 28pt Trebuchet MS\nfont:11:normal normal * Trebuchet MS\nfont:12:normal normal 10pt *\nfont:13:normal normal 10pt Arial\nfont:14:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", - }, - name: "inv2", - hidden: "0", - }, - }, - EditableCells: { - allow: true, - cells: { - "inv1!B2": true, - "inv1!C5": true, - "inv1!C6": true, - "inv1!C7": true, - "inv1!C8": true, - "inv1!C9": true, - "inv1!C11": true, - "inv1!C12": true, - "inv1!C13": true, - "inv1!C14": true, - "inv1!C15": true, - "inv1!C16": true, - "inv1!C18": true, - "inv1!C20": true, - "inv1!D20": true, - "inv1!C23": true, - "inv1!C24": true, - "inv1!C25": true, - "inv1!C26": true, - "inv1!C27": true, - "inv1!C28": true, - "inv1!C29": true, - "inv1!C30": true, - "inv1!C31": true, - "inv1!C32": true, - "inv1!C33": true, - "inv1!C34": true, - "inv1!C35": true, - "inv1!F23": true, - "inv1!F24": true, - "inv1!F25": true, - "inv1!F26": true, - "inv1!F27": true, - "inv1!F28": true, - "inv1!F29": true, - "inv1!F30": true, - "inv1!F31": true, - "inv1!F32": true, - "inv1!F33": true, - "inv1!F34": true, - "inv1!F35": true, "inv2!B2": true, "inv2!F4": true, "inv2!G4": true, @@ -503,52 +335,9 @@ export let DATA: { [key: number]: TemplateData } = { "inv2!F39": true, "inv2!G39": true, "inv2!F38": true, - "inv1!D8": true, - "inv1!D15": true, - "inv1!D18": true, "inv2!C5": true, }, constraints: { - "inv1!C5": ["prompttext", "0", "1e10", "Name"], - "inv1!C6": ["prompttext", "0", "1e10", "Street Address"], - "inv1!C7": ["prompttext", "0", "1e10", "City, State, Zip"], - "inv1!C8": ["prompttext", "0", "1e10", "Phone"], - "inv1!C9": ["promptemail", "0", "1e10", "Email"], - "inv1!C11": ["prompttext", "0", "1e10", "From"], - "inv1!C12": ["prompttext", "0", "1e10", "Name"], - "inv1!C13": ["prompttext", "0", "1e10", "Street Address"], - "inv1!C14": ["prompttext", "0", "1e10", "City, State, Zip"], - "inv1!C15": ["prompttext", "0", "1e10", "Phone"], - "inv1!C16": ["promptemail", "0", "1e10", "Email"], - "inv1!C18": ["prompttext", "0", "1e10", "Invoice #"], - "inv1!C20": ["prompttext", "0", "1e10", "Date"], - "inv1!D20": ["prompttext", "0", "1e10", "Date"], - "inv1!C23": ["prompttext", "0", "1e10", "Description"], - "inv1!C24": ["prompttext", "0", "1e10", "Description"], - "inv1!C25": ["prompttext", "0", "1e10", "Description"], - "inv1!C26": ["prompttext", "0", "1e10", "Description"], - "inv1!C27": ["prompttext", "0", "1e10", "Description"], - "inv1!C28": ["prompttext", "0", "1e10", "Description"], - "inv1!C29": ["prompttext", "0", "1e10", "Description"], - "inv1!C30": ["prompttext", "0", "1e10", "Description"], - "inv1!C31": ["prompttext", "0", "1e10", "Description"], - "inv1!C32": ["prompttext", "0", "1e10", "Description"], - "inv1!C33": ["prompttext", "0", "1e10", "Description"], - "inv1!C34": ["prompttext", "0", "1e10", "Description"], - "inv1!C35": ["prompttext", "0", "1e10", "Description"], - "inv1!F23": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F24": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F25": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F26": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F27": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F28": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F29": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F30": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F31": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F32": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F33": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F34": ["promptdecimal", "0", "1e10", "Amount"], - "inv1!F35": ["promptdecimal", "0", "1e10", "Amount"], "inv2!B2": ["prompttext", "0", "1e10", "Invoice"], "inv2!F4": ["prompttext", "0", "1e10", "Date"], "inv2!G4": ["prompttext", "0", "1e10", "Date"], @@ -601,94 +390,831 @@ export let DATA: { [key: number]: TemplateData } = { "inv2!F39": ["prompttext", "0", "1e10", "Other"], "inv2!G39": ["promptdecimal", "0", "1e10", "Other"], "inv2!F38": ["prompttext", "0", "1e10", "Tax"], - "inv1!D8": ["prompttext", "0", "1e10", "Phone"], - "inv1!D15": ["prompttext", "0", "1e10", "Phone"], - "inv1!D18": ["promptnumeric", "0", "1e10", "Invoice#"], "inv2!C5": ["promptnumeric", "0", "1e10", "Invoice#"], }, }, }, + }, + + 1003: { + template: "Mobile-Multi-Invoice", + templateId: 1003, + footers: [ + { name: "Detail1", index: 1, isActive: false }, + { name: "Detail2", index: 2, isActive: false }, + { name: "Invoice", index: 3, isActive: true }, + ], + logoCell: { + 1: "", + 2: "", + 3: "F8", + }, + signatureCell: { + 1: "", + 2: "", + 3: "F41", + }, + cellMappings: { + 1: { + Heading: "B2", + Items: { + Name: "Items", + Rows: { + start: 6, + end: 18, + }, + Columns: { + Description: "B", + Hours: "E", + Rate: "F", + }, + }, + }, + 2: { + Heading: "B2", + Items: { + Name: "Items", + Rows: { + start: 6, + end: 18, + }, + Columns: { + Description: "B", + Qty: "E", + Price: "F", + }, + }, + }, + 3: { + Heading: "B2", + Date: "G4", + InvoiceNumber: "B5", + From: { + CompanyName: "B8", + StreetAddress: "B9", + CityStateZip: "B10", + Phone: "B11", + Email: "B12", + }, + BillTo: { + Name: "B15", + CompanyName: "B16", + StreetAddress: "B17", + CityStateZip: "B18", + Phone: "B19", + Email: "B20", + }, + TaxPercentage: "G37", + OtherCharges: "G39", + Notes: { + 1: "B38", + 2: "B39", + 3: "B40", + }, + }, + }, + msc: { + numsheets: 3, + currentid: "sheet3", + currentname: "sheet6", + sheetArr: { + sheet1: { + sheetstr: { + savestr: + "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Hours:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Rate:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + }, + name: "inv3", + hidden: "0", + }, + sheet2: { + sheetstr: { + savestr: + "version:1.5\ncell:B1:b:::2::l:1:f:7\ncell:C1:b:::2::l:1:f:7\ncell:D1:b:::2::l:1:f:7\ncell:E1:b:::2::l:1:f:7\ncell:F1:b:::2::l:1:f:7\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE DETAILS:b:1:1:1:1:l:1:f:8:cf:1:colspan:5\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:b:2:1:1::l:1:f:7\ncell:G2:b::::1\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2::::l:1:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:1:1:::l:1:f:7\ncell:G3:b::::1\ncell:A4:b::2:::l:3:f:7\ncell:B4:b:::2::l:1:f:7\ncell:C4:b:::2::l:3:f:4:cf:2\ncell:D4:b:::2::l:1:f:7\ncell:E4:b:::2::l:1:f:7\ncell:F4:b::1:1::l:1:f:7\ncell:G4:b::::1\ncell:A5:b::2:::l:3:f:7\ncell:B5:t:Description:b:1:1:1:1:f:6:cf:1:colspan:3\ncell:C5:t:Description:b:2::2:2:l:3:f:4:cf:2:colspan:2:rowspan:1\ncell:D5:t::b:2:2:2::l:1:f:7\ncell:E5:t:Qty.:b:1:1:1:1:f:6:cf:1\ncell:F5:t:Price:b:1:1:1:1:f:6:cf:1\ncell:G5:b::::1\ncell:A6:b::2:::l:3:f:7\ncell:B6:b:1:1::1:f:1:cf:2:colspan:3\ncell:C6:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:D6:t::b:2:2:2::l:1:f:7\ncell:E6:b:1:1:::f:1:ntvf:1\ncell:F6:b:1:1:::f:1:ntvf:1\ncell:G6:b::::1\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::1::1:f:1:cf:2:colspan:3\ncell:C7:b::1::1:l:1:f:7:colspan:2\ncell:D7:l:1:f:7\ncell:E7:b::1:::f:1:ntvf:1\ncell:F7:b::1:::f:1:ntvf:1\ncell:G7:b::::1\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::1::1:f:1:cf:2:colspan:3\ncell:C8:b::1::1:colspan:2\ncell:E8:b::1:::f:1:ntvf:1\ncell:F8:b::1:::f:1:ntvf:1\ncell:G8:b::::1\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::1::1:f:1:cf:2:colspan:3\ncell:C9:b::1::1:colspan:2\ncell:E9:b::1:::f:1:ntvf:1\ncell:F9:b::1:::f:1:ntvf:1\ncell:G9:b::::1\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::1::1:f:1:cf:2:colspan:3\ncell:C10:b::1::1:colspan:2\ncell:E10:b::1:::f:1:ntvf:1\ncell:F10:b::1:::f:1:ntvf:1\ncell:G10:b::::1\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::1::1:f:1:cf:2:colspan:3\ncell:C11:b::1::1:colspan:2\ncell:E11:b::1:::f:1:ntvf:1\ncell:F11:b::1:::f:1:ntvf:1\ncell:G11:b::::1\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::1::1:f:1:cf:2:colspan:3\ncell:C12:b::1::1:colspan:2\ncell:E12:b::1:::f:1:ntvf:1\ncell:F12:b::1:::f:1:ntvf:1\ncell:G12:b::::1\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::1::1:f:1:cf:2:colspan:3\ncell:C13:b::1::1:colspan:2\ncell:E13:b::1:::f:1:ntvf:1\ncell:F13:b::1:::f:1:ntvf:1\ncell:G13:b::::1\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::1::1:f:1:cf:2:colspan:3\ncell:C14:b::1::1:colspan:2\ncell:E14:b::1:::f:1:ntvf:1\ncell:F14:b::1:::f:1:ntvf:1\ncell:G14:b::::1\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::1::1:f:1:cf:2:colspan:3\ncell:C15:b::1::1:colspan:2\ncell:E15:b::1:::f:1:ntvf:1\ncell:F15:b::1:::f:1:ntvf:1\ncell:G15:b::::1\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::1::1:f:1:cf:2:colspan:3\ncell:C16:b::1::1:colspan:2\ncell:E16:b::1:::f:1:ntvf:1\ncell:F16:b::1:::f:1:ntvf:1\ncell:G16:b::::1\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::1::1:f:1:cf:2:colspan:3\ncell:C17:b::1::1:colspan:2\ncell:E17:b::1:::f:1:ntvf:1\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::::1\ncell:A18:b::2:::l:3:f:5\ncell:B18:b::1:1:1:f:1:cf:2:colspan:3\ncell:C18:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:D18:b:::2::l:3:f:7:cf:2\ncell:E18:b::1:1::f:1:ntvf:1\ncell:F18:b::1:1::f:1:ntvf:1\ncell:G18:b::::1\ncell:A19:b::2:::l:3:f:7\ncell:F19:b::1::\ncell:G19:b::::1\ncell:A20:b::2:::l:3:f:7\ncell:F20:b::1::\ncell:G20:b::::1\ncell:A21:b::2:::l:3:f:7\ncell:C21:t:Thank you for your business:b::1:::f:3:colspan:4\ncell:D21:t:Thank you for your business:f:2:colspan:4\ncell:E21:t::l:2:f:7\ncell:F21:l:2:f:7\ncell:G21:b::::1\ncell:A22:b::2:::l:3:f:7\ncell:F22:b::1::\ncell:G22:b::::1\ncell:A23:b::2:::l:3:f:7\ncell:B23:b:::2::l:3:f:7\ncell:C23:b:::2::l:3:f:7\ncell:D23:b:::2::l:3:f:7\ncell:E23:b:::2::l:3:f:7\ncell:F23:b::1:1::l:3:f:7\ncell:G23:b::::1\ncell:B24:b:1:::\ncell:C24:b:1:::\ncell:D24:b:1:::\ncell:E24:b:1:::\ncell:F24:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:56\ncol:D:w:39\ncol:E:w:48\ncol:F:w:57\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:15.75\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nsheet:c:7:r:24:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:italic bold * Trebuchet MS\nfont:3:italic bold 10pt Trebuchet MS\nfont:4:normal bold 10pt Arial\nfont:5:normal bold 12pt Arial\nfont:6:normal normal * Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 16pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\n", + }, + name: "sheet7", + hidden: "0", + }, + sheet3: { + sheetstr: { + savestr: + 'version:1.5\ncell:B2:t:INVOICE:l:1:f:12:cf:1:colspan:6\ncell:C2:t::l:2:f:10\ncell:D2:t::l:2:f:10\ncell:E2:l:1:f:8:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:10\ncell:B3:f:5:cf:2:colspan:4\ncell:C3:t::l:2:f:10\ncell:D3:t::l:2:f:10\ncell:F3:l:1:f:6:cf:2\ncell:G3:l:1:f:11:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:11:cf:2\ncell:G4:vtf:nd:45897:TODAY():l:1:f:11:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:9:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:6:cf:2:colspan:2\ncell:G5:l:1:f:11:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:6:cf:2:colspan:2\ncell:G6:l:1:f:11:cf:2\ncell:B7:t:FROM\\c:f:11\ncell:F7:l:1:f:6:cf:2:colspan:2\ncell:G7:l:1:f:11:cf:2\ncell:B8:t:[Company Name]:f:9:colspan:4\ncell:F8:l:1:f:6:cf:2:tvf:4:colspan:2:rowspan:5\ncell:G8:l:1:f:11:cf:2\ncell:B9:t:[Street Address]:f:9:cf:2:colspan:4\ncell:F9:l:1:f:6\ncell:G9:l:1:f:11:cf:1\ncell:B10:t:[City, State, Zip]:f:9:cf:2:colspan:4\ncell:G10:l:1:f:10\ncell:B11:t:Phone\\c :f:9:cf:2:colspan:4\ncell:B12:t:Email\\c:f:9:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:9:cf:2\ncell:B15:t:[Name]:f:9:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:9:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:9:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:9:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:9:cf:2:colspan:6\ncell:B20:t:Email\\c:f:9:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:11:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:10\ncell:D22:t::l:2:f:10\ncell:E22:t::l:2:f:10\ncell:F22:t::l:2:f:10\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:11:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), sheet7!B6, inv3!B6)):b:1:1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:3\ncell:D23:t::l:2:f:3\ncell:E23:t::l:2:f:3\ncell:F23:t::b::2:::l:1:f:3\ncell:G23:vtf:t: :IF(AND(ISBLANK(inv3!B6),ISBLANK(sheet7!B6)), " ", IF(ISBLANK(inv3!B6), (sheet7!E6*sheet7!F6), (inv3!E6*inv3!F6))):b::1::1:f:3:ntvf:1\ncell:A24:b::1::\ncell:B24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), SHEET7!B7, INV3!B7)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:3\ncell:D24:t::l:2:f:3\ncell:E24:t::l:2:f:3\ncell:F24:t::b::2:::l:1:f:3\ncell:G24:vtf:t: :IF(AND(ISBLANK(INV3!B7),ISBLANK(SHEET7!B7)), " ", IF(ISBLANK(INV3!B7), (SHEET7!E7*SHEET7!F7), (INV3!E7*INV3!F7))):b::1::1:f:3:ntvf:1\ncell:A25:b::1::\ncell:B25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), SHEET7!B8, INV3!B8)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:3\ncell:D25:t::l:2:f:3\ncell:E25:t::l:2:f:3\ncell:F25:t::b::2:::l:1:f:3\ncell:G25:vtf:t: :IF(AND(ISBLANK(INV3!B8),ISBLANK(SHEET7!B8)), " ", IF(ISBLANK(INV3!B8), (SHEET7!E8*SHEET7!F8), (INV3!E8*INV3!F8))):b::1::1:f:3:ntvf:1\ncell:A26:b::1::\ncell:B26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), SHEET7!B9, INV3!B9)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:3\ncell:D26:t::l:2:f:3\ncell:E26:t::l:2:f:3\ncell:F26:t::b::2:::l:1:f:3\ncell:G26:vtf:t: :IF(AND(ISBLANK(INV3!B9),ISBLANK(SHEET7!B9)), " ", IF(ISBLANK(INV3!B9), (SHEET7!E9*SHEET7!F9), (INV3!E9*INV3!F9))):b::1::1:f:3:ntvf:1\ncell:A27:b::1::\ncell:B27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), SHEET7!B10, INV3!B10)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:3\ncell:D27:t::l:2:f:3\ncell:E27:t::l:2:f:3\ncell:F27:t::b::2:::l:1:f:3\ncell:G27:vtf:t: :IF(AND(ISBLANK(INV3!B10),ISBLANK(SHEET7!B10)), " ", IF(ISBLANK(INV3!B10), (SHEET7!E10*SHEET7!F10), (INV3!E10*INV3!F10))):b::1::1:f:3:ntvf:1\ncell:A28:b::1::\ncell:B28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), SHEET7!B11, INV3!B11)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:3\ncell:D28:t::l:2:f:3\ncell:E28:t::l:2:f:3\ncell:F28:t::b::2:::l:1:f:3\ncell:G28:vtf:t: :IF(AND(ISBLANK(INV3!B11),ISBLANK(SHEET7!B11)), " ", IF(ISBLANK(INV3!B11), (SHEET7!E11*SHEET7!F11), (INV3!E11*INV3!F11))):b::1::1:f:3:ntvf:1\ncell:A29:b::1::\ncell:B29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), SHEET7!B12, INV3!B12)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:3\ncell:D29:t::l:2:f:3\ncell:E29:t::l:2:f:3\ncell:F29:t::b::2:::l:1:f:3\ncell:G29:vtf:t: :IF(AND(ISBLANK(INV3!B12),ISBLANK(SHEET7!B12)), " ", IF(ISBLANK(INV3!B12), (SHEET7!E12*SHEET7!F12), (INV3!E12*INV3!F12))):b::1::1:f:3:ntvf:1\ncell:A30:b::1::\ncell:B30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), SHEET7!B13, INV3!B13)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:3\ncell:D30:t::l:2:f:3\ncell:E30:t::l:2:f:3\ncell:F30:t::b::2:::l:1:f:3\ncell:G30:vtf:t: :IF(AND(ISBLANK(INV3!B13),ISBLANK(SHEET7!B13)), " ", IF(ISBLANK(INV3!B13), (SHEET7!E13*SHEET7!F13), (INV3!E13*INV3!F13))):b::1::1:f:3:ntvf:1\ncell:A31:b::1::\ncell:B31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), SHEET7!B14, INV3!B14)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:3\ncell:D31:t::l:2:f:3\ncell:E31:t::l:2:f:3\ncell:F31:t::b::2:::l:1:f:3\ncell:G31:vtf:t: :IF(AND(ISBLANK(INV3!B14),ISBLANK(SHEET7!B14)), " ", IF(ISBLANK(INV3!B14), (SHEET7!E14*SHEET7!F14), (INV3!E14*INV3!F14))):b::1::1:f:3:ntvf:1\ncell:A32:b::1::\ncell:B32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), SHEET7!B15, INV3!B15)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:3\ncell:D32:t::l:2:f:3\ncell:E32:t::l:2:f:3\ncell:F32:t::b::2:::l:1:f:3\ncell:G32:vtf:t: :IF(AND(ISBLANK(INV3!B15),ISBLANK(SHEET7!B15)), " ", IF(ISBLANK(INV3!B15), (SHEET7!E15*SHEET7!F15), (INV3!E15*INV3!F15))):b::1::1:f:3:ntvf:1\ncell:A33:b::1::\ncell:B33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), SHEET7!B16, INV3!B16)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C33:l:2:f:3\ncell:D33:l:2:f:3\ncell:E33:l:2:f:3\ncell:F33:b::2:::l:1:f:3\ncell:G33:vtf:t: :IF(AND(ISBLANK(INV3!B16),ISBLANK(SHEET7!B16)), " ", IF(ISBLANK(INV3!B16), (SHEET7!E16*SHEET7!F16), (INV3!E16*INV3!F16))):b::1::1:f:3:ntvf:1\ncell:A34:b::1::\ncell:B34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), SHEET7!B17, INV3!B17)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C34:t::l:2:f:3\ncell:D34:t::l:2:f:3\ncell:E34:t::l:2:f:3\ncell:F34:t::b::2:::l:1:f:3\ncell:G34:vtf:t: :IF(AND(ISBLANK(INV3!B17),ISBLANK(SHEET7!B17)), " ", IF(ISBLANK(INV3!B17), (SHEET7!E17*SHEET7!F17), (INV3!E17*INV3!F17))):b::1::1:f:3:ntvf:1\ncell:A35:b::1::\ncell:B35:vtf:t: :IF(AND(ISBLANK(INV3!B18), ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), SHEET7!B18, INV3!B18)):b::1::1:f:3:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:3\ncell:D35:t::b:::2::l:1:f:3\ncell:E35:t::b:::2::l:1:f:3\ncell:F35:t::b::2:2::l:1:f:3\ncell:G35:vtf:t: :IF(AND(ISBLANK(INV3!B18),ISBLANK(SHEET7!B18)), " ", IF(ISBLANK(INV3!B18), (SHEET7!E18*SHEET7!F18), (INV3!E18*INV3!F18))):b::1::1:f:3:ntvf:1\ncell:B36:b:2::::l:1:f:10\ncell:C36:b:2::::l:1:f:10\ncell:D36:b:2::::l:1:f:10\ncell:E36:b:2::::l:1:f:11\ncell:F36:t:Subtotal:b:2::::l:1:f:9\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:9:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:11:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:10\ncell:D37:t::b:::2::l:1:f:10\ncell:F37:t:Tax Rate:l:1:f:9\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:10\ncell:D38:t::b:2::::l:1:f:10\ncell:F38:t:Tax:l:1:f:9\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:10\ncell:D39:t::l:2:f:10\ncell:F39:t:Other:b:::1::l:1:f:9\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:10\ncell:D40:t::l:2:f:10\ncell:F40:t:TOTAL:b:1::::l:1:f:11\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:9:ntvf:1\ncell:F41:colspan:2:rowspan:2\ncell:B43:t:Thank you for your business:l:1:f:4:cf:1:colspan:6:rowspan:1\ncell:C43:t::l:2:f:10\ncell:D43:t::l:2:f:10\ncell:E43:t::l:2:f:10\ncell:F43:t::l:2:f:10\ncell:G43:t::l:2:f:10\ncol:A:w:10\ncol:B:w:65\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:53\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 9pt Trebuchet MS\nfont:4:italic bold 10pt Trebuchet MS\nfont:5:italic normal * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 14pt Trebuchet MS\nfont:8:normal bold 28pt Trebuchet MS\nfont:9:normal normal * Trebuchet MS\nfont:10:normal normal 10pt Arial\nfont:11:normal normal 10pt Trebuchet MS\nfont:12:normal normal 16pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', + }, + name: "sheet6", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "sheet6!B2": true, + "sheet6!F4": true, + "sheet6!G4": true, + "sheet6!B5": true, + "sheet6!B7": true, + "sheet6!B8": true, + "sheet6!B9": true, + "sheet6!B10": true, + "sheet6!B11": true, + "sheet6!B12": true, + "sheet6!B14": true, + "sheet6!B15": true, + "sheet6!B16": true, + "sheet6!B17": true, + "sheet6!B18": true, + "sheet6!B19": true, + "sheet6!B20": true, + "sheet6!B38": true, + "sheet6!B39": true, + "sheet6!B40": true, + "sheet6!F36": true, + "sheet6!G37": true, + "sheet6!F37": true, + "sheet6!F39": true, + "sheet6!G39": true, + "sheet6!F38": true, + "inv3!B2": true, + "inv3!B6": true, + "inv3!B7": true, + "inv3!B8": true, + "inv3!B9": true, + "inv3!B10": true, + "inv3!B11": true, + "inv3!B12": true, + "inv3!B13": true, + "inv3!B14": true, + "inv3!B15": true, + "inv3!B16": true, + "inv3!B17": true, + "inv3!B18": true, + "inv3!E6": true, + "inv3!E7": true, + "inv3!E8": true, + "inv3!E9": true, + "inv3!E10": true, + "inv3!E11": true, + "inv3!E12": true, + "inv3!E13": true, + "inv3!E14": true, + "inv3!E15": true, + "inv3!E16": true, + "inv3!E17": true, + "inv3!E18": true, + "inv3!F6": true, + "inv3!F7": true, + "inv3!F8": true, + "inv3!F9": true, + "inv3!F10": true, + "inv3!F11": true, + "inv3!F12": true, + "inv3!F13": true, + "inv3!F14": true, + "inv3!F15": true, + "inv3!F16": true, + "inv3!F17": true, + "inv3!F18": true, + "sheet7!F6": true, + "sheet7!F7": true, + "sheet7!F8": true, + "sheet7!F9": true, + "sheet7!F10": true, + "sheet7!F11": true, + "sheet7!F12": true, + "sheet7!F13": true, + "sheet7!F14": true, + "sheet7!F15": true, + "sheet7!F16": true, + "sheet7!F17": true, + "sheet7!F18": true, + "sheet7!E6": true, + "sheet7!E7": true, + "sheet7!E8": true, + "sheet7!E9": true, + "sheet7!E10": true, + "sheet7!E11": true, + "sheet7!E12": true, + "sheet7!E13": true, + "sheet7!E14": true, + "sheet7!E15": true, + "sheet7!E16": true, + "sheet7!E17": true, + "sheet7!E18": true, + "sheet7!B6": true, + "sheet7!B2": true, + "sheet7!B7": true, + "sheet7!B8": true, + "sheet7!B9": true, + "sheet7!B10": true, + "sheet7!B11": true, + "sheet7!B12": true, + "sheet7!B13": true, + "sheet7!B14": true, + "sheet7!B15": true, + "sheet7!B16": true, + "sheet7!B17": true, + "sheet7!B18": true, + "sheet6!C5": true, + }, + constraints: { + "sheet6!B2": ["prompttext", "0", "1e10", "Invoice"], + "sheet6!F4": ["prompttext", "0", "1e10", "Date"], + "sheet6!G4": ["prompttext", "0", "1e10", "Date"], + "sheet6!B5": ["prompttext", "0", "1e10", "Invoice #"], + "sheet6!B7": ["prompttext", "0", "1e10", "From"], + "sheet6!B8": ["prompttext", "0", "1e10", "Company Name"], + "sheet6!B9": ["prompttext", "0", "1e10", "Street Address"], + "sheet6!B10": ["prompttext", "0", "1e10", "City, State, Zip"], + "sheet6!B11": ["prompttext", "0", "1e10", "Phone"], + "sheet6!B12": ["promptemail", "0", "1e10", "Email"], + "sheet6!B14": ["prompttext", "0", "1e10", "Bill To"], + "sheet6!B15": ["prompttext", "0", "1e10", "Name"], + "sheet6!B16": ["prompttext", "0", "1e10", "Company Name"], + "sheet6!B17": ["prompttext", "0", "1e10", "Street Address"], + "sheet6!B18": ["prompttext", "0", "1e10", "City, State, Zip"], + "sheet6!B19": ["prompttext", "0", "1e10", "Phone"], + "sheet6!B20": ["promptemail", "0", "1e10", "Email"], + "sheet6!B38": ["prompttext", "0", "1e10", "Notes"], + "sheet6!B39": ["prompttext", "0", "1e10", "Notes"], + "sheet6!B40": ["prompttext", "0", "1e10", "Notes"], + "sheet6!F36": ["prompttext", "0", "1e10", "Subtotal"], + "sheet6!G37": ["promptdecimal", "0", "1e10", "Tax Rate (0.00)"], + "sheet6!F37": ["prompttext", "0", "1e10", "Tax Rate"], + "sheet6!F39": ["prompttext", "0", "1e10", "Other"], + "sheet6!G39": ["promptdecimal", "0", "1e10", "Other"], + "sheet6!F38": ["prompttext", "0", "1e10", "Tax"], + "inv3!B6": ["prompttext", "0", "1e10", "Description"], + "inv3!B7": ["prompttext", "0", "1e10", "Description"], + "inv3!B8": ["prompttext", "0", "1e10", "Description"], + "inv3!B9": ["prompttext", "0", "1e10", "Description"], + "inv3!B10": ["prompttext", "0", "1e10", "Description"], + "inv3!B11": ["prompttext", "0", "1e10", "Description"], + "inv3!B12": ["prompttext", "0", "1e10", "Description"], + "inv3!B13": ["prompttext", "0", "1e10", "Description"], + "inv3!B14": ["prompttext", "0", "1e10", "Description"], + "inv3!B15": ["prompttext", "0", "1e10", "Description"], + "inv3!B16": ["prompttext", "0", "1e10", "Description"], + "inv3!B17": ["prompttext", "0", "1e10", "Description"], + "inv3!B18": ["prompttext", "0", "1e10", "Description"], + "sheet7!B6": ["prompttext", "0", "1e10", "Description"], + "sheet7!B7": ["prompttext", "0", "1e10", "Description"], + "sheet7!B8": ["prompttext", "0", "1e10", "Description"], + "sheet7!B9": ["prompttext", "0", "1e10", "Description"], + "sheet7!B10": ["prompttext", "0", "1e10", "Description"], + "sheet7!B11": ["prompttext", "0", "1e10", "Description"], + "sheet7!B12": ["prompttext", "0", "1e10", "Description"], + "sheet7!B13": ["prompttext", "0", "1e10", "Description"], + "sheet7!B14": ["prompttext", "0", "1e10", "Description"], + "sheet7!B15": ["prompttext", "0", "1e10", "Description"], + "sheet7!B16": ["prompttext", "0", "1e10", "Description"], + "sheet7!B17": ["prompttext", "0", "1e10", "Description"], + "sheet7!B18": ["prompttext", "0", "1e10", "Description"], + "sheet6!C5": ["promptnumeric", "0", "1e10", "Invoice#"], + "sheet7!E6": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E7": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E8": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E9": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E10": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E11": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E12": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E13": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E14": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E15": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E16": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E17": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!E18": ["promptdecimal", "0", "1e10", "Quantity"], + "sheet7!F6": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F7": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F8": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F9": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F10": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F11": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F12": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F13": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F14": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F15": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F16": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F17": ["promptdecimal", "0", "1e10", "Price"], + "sheet7!F18": ["promptdecimal", "0", "1e10", "Price"], + "inv3!E6": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E7": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E8": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E9": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E10": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E11": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E12": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E13": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E14": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E15": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E16": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E17": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!E18": ["promptdecimal", "0", "1e10", "Hours"], + "inv3!F6": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F7": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F8": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F9": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F10": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F11": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F12": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F13": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F14": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F15": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F16": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F17": ["promptdecimal", "0", "1e10", "Rate"], + "inv3!F18": ["promptdecimal", "0", "1e10", "Rate"], + }, + }, + }, + }, - footers: [ - { name: "Invoice 1", index: 1, isActive: true }, - { name: "Invoice 2", index: 2, isActive: false }, - ], + 3001: { + template: "Web-Invoice-1", + templateId: 3001, + footers: [{ name: "Invoice", index: 1, isActive: true }], logoCell: { - 1: "F7", - 2: "F7", + 1: "F4", }, signatureCell: { - 1: "", - 2: "", + 1: "D31", }, cellMappings: { 1: { Heading: "B2", - Date: "G4", - InvoiceNumber: "C18", + Date: "D6", + InvoiceNumber: "D4", From: { - Name: "C12", - StreetAddress: "C13", - CityStateZip: "C14", - Phone: "D15", - Email: "C16", + Name: "E10", + StreetAddress: "E11", + CityStateZip: "E12", + Phone: "E13", }, BillTo: { - Name: "C5", - StreetAddress: "C6", - CityStateZip: "C7", - Phone: "C8", - Email: "C9", + Name: "C10", + StreetAddress: "C11", + CityStateZip: "C12", + Phone: "C13", }, Items: { - name: "Items", - Range: { - start: 23, - end: 35, + Name: "Items", + Rows: { + start: 16, + end: 28, }, - Content: { + Columns: { Description: "C", Amount: "F", }, }, - Notes: "B39", }, - 2: { + }, + msc: { + numsheets: 1, + currentid: "sheet1", + currentname: "typei", + sheetArr: { + sheet1: { + sheetstr: { + savestr: + "version:1.5\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE:b:1:1:1:1:f:6:cf:1:colspan:6\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:t::b:2::2::l:1:f:7\ncell:G2:t::b:2::2::l:1:f:7\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2:::2:l:3:f:7\ncell:C3:b:2::::l:1:f:7\ncell:D3:b:2::::l:1:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:2::::l:1:f:7\ncell:G3:b:2:2:::l:3:f:7\ncell:A4:b::2:::l:3:f:7\ncell:B4:b::::2:l:3:f:3\ncell:C4:t:INVOICE # \\c:f:2:cf:2\ncell:D4:v:1:f:2:cf:2\ncell:F4:tvf:4:rowspan:4\ncell:G4:b::2:::l:3:f:7\ncell:A5:b::2:::l:3:f:7\ncell:B5:b::::2:l:3:f:4\ncell:F5:t::l:2:f:7\ncell:G5:t::b::1:::l:2:f:7\ncell:A6:b::2:::l:3:f:7\ncell:B6:b::::2:l:3:f:4\ncell:C6:t:INVOICE DATE\\c:f:2:cf:2\ncell:D6:f:2:cf:2:ntvf:3\ncell:F6:l:2:f:7\ncell:G6:b::1:::l:2:f:7\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::::2:l:3:f:4\ncell:G7:b::2:::l:3:f:7\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::::2:l:3:f:4\ncell:G8:b::2:::l:3:f:7\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::::2:l:3:f:4\ncell:C9:t:BILL TO\\c:l:3:f:2\ncell:E9:t:FROM\\c:f:2:cf:2\ncell:G9:b::2:::l:3:f:7\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::::2:l:3:f:3\ncell:C10:t:[Name]:f:1:cf:2:colspan:2\ncell:E10:t:[Name]:f:1:cf:2:colspan:2\ncell:G10:b::2:::l:3:f:7\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:E11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:G11:b::2:::l:3:f:7\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:E12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:G12:b::2:::l:3:f:7\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:Phone\\c:f:1:cf:2:colspan:2\ncell:E13:t:Phone\\c:f:1:cf:2:colspan:2\ncell:G13:b::2:::l:3:f:7\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::::2:l:3:f:3:cf:2\ncell:C14:b:::2::l:1:f:7\ncell:D14:b:::2::l:3:f:3:cf:2\ncell:E14:b:::2::l:1:f:7\ncell:F14:b:::2::l:1:f:7\ncell:G14:b::2:::l:3:f:7\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::2::2:l:3:f:7\ncell:C15:t:Description:b:1::1:1:f:2:cf:1:colspan:3\ncell:D15:t:Description:b:1::1::l:3:f:3:cf:2:colspan:2:rowspan:1\ncell:E15:t::b:2:2:2::l:1:f:7\ncell:F15:t:Amount:b:1:1:1:1:f:2:cf:1\ncell:G15:b::2::2:l:3:f:7\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::2::2:l:3:f:7\ncell:C16:b:1:1::1:f:1:cf:2:colspan:3\ncell:D16:b:1:1:::l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:E16:t::b:2:2:2::l:1:f:7\ncell:F16:b:1:1:::f:1:ntvf:1\ncell:G16:b::2::2:l:3:f:7\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::2::2:l:3:f:7:cf:2\ncell:C17:b::1::1:f:1:cf:2:colspan:3\ncell:D17:b::1:::l:1:f:7:colspan:2\ncell:E17:l:1:f:7\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::2::2:l:3:f:7\ncell:A18:b::2:::l:3:f:7\ncell:B18:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C18:b::1::1:f:1:cf:2:colspan:3\ncell:D18:b::1:::colspan:2\ncell:F18:b::1:::f:1:ntvf:1\ncell:G18:b::2::2:l:3:f:7\ncell:A19:b::2:::l:3:f:7\ncell:B19:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C19:b::1::1:f:1:cf:2:colspan:3\ncell:D19:b::1:::colspan:2\ncell:F19:b::1:::f:1:ntvf:1\ncell:G19:b::2::2:l:3:f:7\ncell:A20:b::2:::l:3:f:7\ncell:B20:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C20:b::1::1:f:1:cf:2:colspan:3\ncell:D20:b::1:::colspan:2\ncell:F20:b::1:::f:1:ntvf:1\ncell:G20:b::2::2:l:3:f:7\ncell:A21:b::2:::l:3:f:7\ncell:B21:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C21:b::1::1:f:1:cf:2:colspan:3\ncell:D21:b::1:::colspan:2\ncell:F21:b::1:::f:1:ntvf:1\ncell:G21:b::2::2:l:3:f:7\ncell:A22:b::2:::l:3:f:7\ncell:B22:b::2::2:l:3:f:7:cf:2:ntvf:2\ncell:C22:b::1::1:f:1:cf:2:colspan:3\ncell:D22:b::1:::colspan:2\ncell:F22:b::1:::f:1:ntvf:1\ncell:G22:b::2::2:l:3:f:7\ncell:A23:b::2:::l:3:f:7\ncell:B23:b::2::2:l:3:f:7:cf:2\ncell:C23:b::1::1:f:1:cf:2:colspan:3\ncell:D23:b::1:::colspan:2\ncell:F23:b::1:::f:1:ntvf:1\ncell:G23:b::2::2:l:3:f:7\ncell:A24:b::2:::l:3:f:7\ncell:B24:b::2::2:l:3:f:7:cf:2\ncell:C24:b::1::1:f:1:cf:2:colspan:3\ncell:D24:b::1:::colspan:2\ncell:F24:b::1:::f:1:ntvf:1\ncell:G24:b::2::2:l:3:f:7\ncell:A25:b::2:::l:3:f:7\ncell:B25:b::2::2:l:3:f:7:cf:2\ncell:C25:b::1::1:f:1:cf:2:colspan:3\ncell:D25:b::1:::colspan:2\ncell:F25:b::1:::f:1:ntvf:1\ncell:G25:b::2::2:l:3:f:7\ncell:A26:b::2:::l:3:f:7\ncell:B26:b::2::2:l:3:f:7:cf:2\ncell:C26:b::1::1:f:1:cf:2:colspan:3\ncell:D26:b::1:::colspan:2\ncell:F26:b::1:::f:1:ntvf:1\ncell:G26:b::2::2:l:3:f:7\ncell:A27:b::2:::l:3:f:7\ncell:B27:b::2::2:l:3:f:7:cf:2\ncell:C27:b::1::1:f:1:cf:2:colspan:3\ncell:D27:b::1:::colspan:2\ncell:F27:b::1:::f:1:ntvf:1\ncell:G27:b::2::2:l:3:f:7\ncell:A28:b::2:::l:3:f:4\ncell:B28:b::2::2:l:3:f:7:cf:2\ncell:C28:b::1:1:1:f:1:cf:2:colspan:3\ncell:D28:b::1:1::l:3:f:7:cf:2:colspan:2\ncell:E28:b:::2::l:3:f:7:cf:2\ncell:F28:b::1:1::f:1:ntvf:1\ncell:G28:b::2::2:l:3:f:7\ncell:A29:b::2:::l:3:f:3\ncell:B29:b::2::2:l:3:f:3\ncell:C29:t:TOTAL:b:1:1:1:1:f:5:cf:2:colspan:3\ncell:D29:b:2::2::l:3:f:8:cf:2\ncell:E29:b:2:2:2::l:3:f:8:cf:2\ncell:F29:vtf:n:0:SUM(F16\\cF28):b:1:1:1::f:5:ntvf:1\ncell:G29:b::2::2:l:3:f:4\ncell:A30:b::2:::l:3:f:7\ncell:B30:b::::2:l:3:f:3:cf:2\ncell:C30:b:2::::l:1:f:7\ncell:D30:b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:7\ncell:F30:b:2::::l:1:f:7\ncell:G30:b::2:::l:3:f:3\ncell:A31:b::2:::l:3:f:7\ncell:B31:b::::2:l:3:f:7\ncell:D31:tvf:4:rowspan:4\ncell:G31:b::2:::l:3:f:7\ncell:A32:b::2:::l:3:f:7\ncell:B32:b::::2:l:3:f:7\ncell:G32:b::2:::l:3:f:7\ncell:A33:b::2:::l:3:f:7\ncell:B33:b::::2:l:3:f:7\ncell:G33:b::2:::l:3:f:7\ncell:A34:b::2:::l:3:f:7\ncell:B34:b::::2:l:3:f:7\ncell:G34:b::2:::l:3:f:7\ncell:A35:b::2:::l:3:f:7\ncell:B35:b:::2:2:l:3:f:7\ncell:C35:b:::2::l:3:f:7\ncell:D35:b:::2::l:3:f:7\ncell:E35:b:::2::l:3:f:7\ncell:F35:b:::2::l:3:f:7\ncell:G35:b::2:2::l:3:f:7\ncell:B36:b:1:::\ncell:C36:b:1:::\ncell:D36:b:1:::\ncell:E36:b:1:::\ncell:F36:b:1:::\ncell:G36:b:1:::\ncol:A:w:10\ncol:B:w:42\ncol:C:w:105\ncol:D:w:182\ncol:E:w:110\ncol:F:w:115\ncol:G:w:65\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:15.75\nrow:29:h:15.75\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:7:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:normal bold * Trebuchet MS\nfont:3:normal bold 10pt Arial\nfont:4:normal bold 12pt Arial\nfont:5:normal bold 12pt Trebuchet MS\nfont:6:normal bold 14pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 12pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\nvalueformat:2:d-mmm\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + }, + name: "typei", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "typei!C10": true, + "typei!B2": true, + "typei!C11": true, + "typei!C12": true, + "typei!C13": true, + "typei!D9": true, + "typei!D4": true, + "typei!D6": true, + "typei!E10": true, + "typei!E11": true, + "typei!E12": true, + "typei!E13": true, + "typei!F9": true, + "typei!C16": true, + "typei!C17": true, + "typei!C18": true, + "typei!C19": true, + "typei!C20": true, + "typei!C21": true, + "typei!C22": true, + "typei!C23": true, + "typei!C24": true, + "typei!C25": true, + "typei!C26": true, + "typei!C27": true, + "typei!C28": true, + "typei!F16": true, + "typei!F17": true, + "typei!F18": true, + "typei!F19": true, + "typei!F20": true, + "typei!F21": true, + "typei!F22": true, + "typei!F23": true, + "typei!F24": true, + "typei!F25": true, + "typei!F26": true, + "typei!F27": true, + "typei!F28": true, + "typei!E35": true, + "typei!C4": true, + "typei!C6": true, + "typei!F4": true, + "typei!F29": true, + }, + constraints: {}, + }, + }, + }, + + 3002: { + template: "Web-Invoice-2", + templateId: 3002, + footers: [{ name: "Invoice", index: 1, isActive: true }], + logoCell: { + 1: "F4", + }, + signatureCell: { + 1: "D31", + }, + cellMappings: { + 1: { Heading: "B2", - Date: "G4", - InvoiceNumber: "B5", + Date: "D6", + InvoiceNumber: "D4", From: { - Name: "B8", - StreetAddress: "B9", - CityStateZip: "B10", - Phone: "B11", - Email: "B12", + Name: "E10", + StreetAddress: "E11", + CityStateZip: "E12", + Phone: "E13", }, BillTo: { - Name: "B15", - StreetAddress: "B17", - CityStateZip: "B18", - Phone: "B19", - Email: "B20", + Name: "C10", + StreetAddress: "C11", + CityStateZip: "C12", + Phone: "C13", }, Items: { - name: "Items", - Range: { - start: 23, - end: 35, + Name: "Items", + Rows: { + start: 16, + end: 28, + }, + Columns: { + Description: "C", + Hours: "F", + Rate: "G", + }, + }, + }, + }, + msc: { + numsheets: 1, + currentid: "sheet1", + currentname: "typeii", + sheetArr: { + sheet1: { + sheetstr: { + savestr: + 'version:1.5\ncell:A2:b::2:::l:1:f:7\ncell:B2:t:INVOICE:b:1:1:1:1:l:3:f:6:cf:1:colspan:8\ncell:C2:t::b:2::2::l:1:f:7\ncell:D2:t::b:2::2::l:1:f:7\ncell:E2:t::b:2::2::l:1:f:7\ncell:F2:t::b:2::2::l:1:f:7\ncell:G2:b:2::2::l:1:f:7\ncell:H2:b:2::2::l:1:f:7\ncell:I2:t::b:2::2::l:1:f:7\ncell:A3:b::2:::l:3:f:7\ncell:B3:b:2:::2:l:3:f:7\ncell:E3:b:2::::l:1:f:7\ncell:F3:b:2::::l:1:f:7\ncell:G3:b:2::::l:1:f:7\ncell:H3:b:2::::l:1:f:7\ncell:I3:b:2:2:::l:3:f:7\ncell:A4:b::2:::l:3:f:7\ncell:B4:b::::2:l:3:f:3\ncell:C4:t:INVOICE # \\c:f:2:cf:2\ncell:D4:v:1:f:2:cf:2\ncell:F4:tvf:5:colspan:2:rowspan:4\ncell:I4:b::2:::l:3:f:7\ncell:A5:b::2:::l:3:f:7\ncell:B5:b::::2:l:3:f:4\ncell:F5:t::l:2:f:7\ncell:G5:l:2:f:7\ncell:H5:l:2:f:7\ncell:I5:t::b::1:::l:2:f:7\ncell:A6:b::2:::l:3:f:7\ncell:B6:b::::2:l:3:f:4\ncell:C6:t:INVOICE DATE\\c:f:2:cf:2\ncell:D6:f:2:cf:2:ntvf:4\ncell:F6:l:2:f:7\ncell:G6:l:2:f:7\ncell:H6:l:2:f:7\ncell:I6:b::1:::l:2:f:7\ncell:A7:b::2:::l:3:f:7\ncell:B7:b::::2:l:3:f:4\ncell:I7:b::2:::l:3:f:7\ncell:A8:b::2:::l:3:f:7\ncell:B8:b::::2:l:3:f:4\ncell:I8:b::2:::l:3:f:7\ncell:A9:b::2:::l:3:f:7\ncell:B9:b::::2:l:3:f:4\ncell:C9:t:BILL TO\\c:f:2\ncell:E9:t:FROM\\c:f:2:cf:2\ncell:F9:colspan:3\ncell:I9:b::2:::l:3:f:7\ncell:A10:b::2:::l:3:f:7\ncell:B10:b::::2:l:3:f:3\ncell:C10:t:[Name]:f:1:cf:2:colspan:2\ncell:E10:t:[Name]:f:1:cf:2:colspan:4\ncell:I10:b::2:::l:3:f:7\ncell:A11:b::2:::l:3:f:7\ncell:B11:b::::2:l:3:f:7\ncell:C11:t:[Street Address]:f:1:cf:2:colspan:2\ncell:E11:t:[Street Address]:f:1:colspan:4\ncell:I11:b::2:::l:3:f:7\ncell:A12:b::2:::l:3:f:7\ncell:B12:b::::2:l:3:f:7\ncell:C12:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:E12:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:I12:b::2:::l:3:f:7\ncell:A13:b::2:::l:3:f:7\ncell:B13:b::::2:l:3:f:7\ncell:C13:t:Phone\\c :f:1:cf:2:colspan:2\ncell:E13:t:Phone\\c :f:1:cf:2:colspan:4\ncell:I13:b::2:::l:3:f:7\ncell:A14:b::2:::l:3:f:7\ncell:B14:b::::2:l:3:f:3:cf:2\ncell:C14:b:::2::l:1:f:7\ncell:D14:b:::2::l:3:f:3:cf:2\ncell:E14:b:::2::l:1:f:7\ncell:F14:b:::2::l:1:f:7\ncell:G14:b:::2::l:1:f:7\ncell:H14:b:::2::l:1:f:7\ncell:I14:b::2:::l:3:f:7\ncell:A15:b::2:::l:3:f:7\ncell:B15:b::2::2:l:3:f:7\ncell:C15:t:Description:b:1:1:1:1:f:2:cf:1:colspan:3\ncell:D15:t:Description:b:2::2:2:l:3:f:3:cf:2:colspan:2:rowspan:1\ncell:E15:t::b:2:2:2::l:1:f:7\ncell:F15:t:Hours:b:1:1:1:1:f:2:cf:1\ncell:G15:t:Rate:b:1:1:1:1:f:2:cf:1\ncell:H15:t:Amount:b:1:1:1:1:f:2:cf:1\ncell:I15:b::2::2:l:3:f:7\ncell:A16:b::2:::l:3:f:7\ncell:B16:b::2::2:l:3:f:7\ncell:C16:b:1:1::1:f:1:cf:2:colspan:3\ncell:D16:b:1:1::1:l:3:f:7:cf:2:colspan:2:rowspan:1\ncell:E16:t::b:2:2:2::l:1:f:7\ncell:F16:b:1:1:::f:1:ntvf:1\ncell:G16:b:1:1:::f:1:ntvf:1\ncell:H16:vtf:t::IF(F16*G16>0,F16*G16,""):b:1:1:::f:1:ntvf:1\ncell:I16:b::2::2:l:3:f:7\ncell:A17:b::2:::l:3:f:7\ncell:B17:b::2::2:l:3:f:7:cf:2\ncell:C17:b::1::1:f:1:cf:2:colspan:3\ncell:D17:b::1::1:l:1:f:7:colspan:2\ncell:E17:l:1:f:7\ncell:F17:b::1:::f:1:ntvf:1\ncell:G17:b::1:::f:1:ntvf:1\ncell:H17:vtf:t::IF(F17*G17>0,F17*G17,""):b::1:::f:1:ntvf:1\ncell:I17:b::2::2:l:3:f:7\ncell:A18:b::2:::l:3:f:7\ncell:B18:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C18:b::1::1:f:1:cf:2:colspan:3\ncell:D18:b::1::1:colspan:2\ncell:F18:b::1:::f:1:ntvf:1\ncell:G18:b::1:::f:1:ntvf:1\ncell:H18:vtf:t::IF(F18*G18>0,F18*G18,""):b::1:::f:1:ntvf:1\ncell:I18:b::2::2:l:3:f:7\ncell:A19:b::2:::l:3:f:7\ncell:B19:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C19:b::1::1:f:1:cf:2:colspan:3\ncell:D19:b::1::1:colspan:2\ncell:F19:b::1:::f:1:ntvf:1\ncell:G19:b::1:::f:1:ntvf:1\ncell:H19:vtf:t::IF(F19*G19>0,F19*G19,""):b::1:::f:1:ntvf:1\ncell:I19:b::2::2:l:3:f:7\ncell:A20:b::2:::l:3:f:7\ncell:B20:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C20:b::1::1:f:1:cf:2:colspan:3\ncell:D20:b::1::1:colspan:2\ncell:F20:b::1:::f:1:ntvf:1\ncell:G20:b::1:::f:1:ntvf:1\ncell:H20:vtf:t::IF(F20*G20>0,F20*G20,""):b::1:::f:1:ntvf:1\ncell:I20:b::2::2:l:3:f:7\ncell:A21:b::2:::l:3:f:7\ncell:B21:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C21:b::1::1:f:1:cf:2:colspan:3\ncell:D21:b::1::1:colspan:2\ncell:F21:b::1:::f:1:ntvf:1\ncell:G21:b::1:::f:1:ntvf:1\ncell:H21:vtf:t::IF(F21*G21>0,F21*G21,""):b::1:::f:1:ntvf:1\ncell:I21:b::2::2:l:3:f:7\ncell:A22:b::2:::l:3:f:7\ncell:B22:b::2::2:l:3:f:7:cf:2:ntvf:3\ncell:C22:b::1::1:f:1:cf:2:colspan:3\ncell:D22:b::1::1:colspan:2\ncell:F22:b::1:::f:1:ntvf:1\ncell:G22:b::1:::f:1:ntvf:1\ncell:H22:vtf:t::IF(F22*G22>0,F22*G22,""):b::1:::f:1:ntvf:1\ncell:I22:b::2::2:l:3:f:7\ncell:A23:b::2:::l:3:f:7\ncell:B23:b::2::2:l:3:f:7:cf:2\ncell:C23:b::1::1:f:1:cf:2:colspan:3\ncell:D23:b::1::1:colspan:2\ncell:F23:b::1:::f:1:ntvf:1\ncell:G23:b::1:::f:1:ntvf:1\ncell:H23:vtf:t::IF(F23*G23>0,F23*G23,""):b::1:::f:1:ntvf:1\ncell:I23:b::2::2:l:3:f:7\ncell:A24:b::2:::l:3:f:7\ncell:B24:b::2::2:l:3:f:7:cf:2\ncell:C24:b::1::1:f:1:cf:2:colspan:3\ncell:D24:b::1::1:colspan:2\ncell:F24:b::1:::f:1:ntvf:1\ncell:G24:b::1:::f:1:ntvf:1\ncell:H24:vtf:t::IF(F24*G24>0,F24*G24,""):b::1:::f:1:ntvf:1\ncell:I24:b::2::2:l:3:f:7\ncell:A25:b::2:::l:3:f:7\ncell:B25:b::2::2:l:3:f:7:cf:2\ncell:C25:b::1::1:f:1:cf:2:colspan:3\ncell:D25:b::1::1:colspan:2\ncell:F25:b::1:::f:1:ntvf:1\ncell:G25:b::1:::f:1:ntvf:1\ncell:H25:vtf:t::IF(F25*G25>0,F25*G25,""):b::1:::f:1:ntvf:1\ncell:I25:b::2::2:l:3:f:7\ncell:A26:b::2:::l:3:f:7\ncell:B26:b::2::2:l:3:f:7:cf:2\ncell:C26:b::1::1:f:1:cf:2:colspan:3\ncell:D26:b::1::1:colspan:2\ncell:F26:b::1:::f:1:ntvf:1\ncell:G26:b::1:::f:1:ntvf:1\ncell:H26:vtf:t::IF(F26*G26>0,F26*G26,""):b::1:::f:1:ntvf:1\ncell:I26:b::2::2:l:3:f:7\ncell:A27:b::2:::l:3:f:7\ncell:B27:b::2::2:l:3:f:7:cf:2\ncell:C27:b::1::1:f:1:cf:2:colspan:3\ncell:D27:b::1::1:colspan:2\ncell:F27:b::1:::f:1:ntvf:1\ncell:G27:b::1:::f:1:ntvf:1\ncell:H27:vtf:t::IF(F27*G27>0,F27*G27,""):b::1:::f:1:ntvf:1\ncell:I27:b::2::2:l:3:f:7\ncell:A28:b::2:::l:3:f:4\ncell:B28:b::2::2:l:3:f:7:cf:2\ncell:C28:b::1:1:1:f:1:cf:2:colspan:3\ncell:D28:b::1:1:1:l:3:f:7:cf:2:colspan:2\ncell:E28:b:::2::l:3:f:7:cf:2\ncell:F28:b::1:1::f:1:ntvf:1\ncell:G28:b::1:1::f:1:ntvf:1\ncell:H28:vtf:t::IF(F28*G28>0,F28*G28,""):b::1:::f:1:ntvf:1\ncell:I28:b::2::2:l:3:f:7\ncell:A29:b::2:::l:3:f:3\ncell:B29:b::2::2:l:3:f:3\ncell:C29:t:TOTAL:b:1:1:1:1:l:3:f:5:cf:2:colspan:5\ncell:D29:b:2::2::l:3:f:8:cf:2\ncell:E29:b:2:2:2::l:3:f:8:cf:2\ncell:F29:vtf:n:0:SUM(F16\\cF28):b:2:2:2::l:3:f:4:ntvf:2\ncell:G29:b:2:2:2::l:3:f:4:ntvf:2\ncell:H29:vtf:n:0:SUM(H16\\cH28):b:1:1:1::f:5:ntvf:1\ncell:I29:b::2::2:l:3:f:4\ncell:A30:b::2:::l:3:f:7\ncell:B30:b::::2:l:3:f:3:cf:2\ncell:C30:b:2::::l:1:f:7\ncell:D30:b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:7\ncell:F30:b:2::::l:1:f:7\ncell:G30:b:2::::l:1:f:7\ncell:H30:b:2::::l:1:f:7\ncell:I30:b::2:::l:3:f:3\ncell:A31:b::2:::l:3:f:7\ncell:B31:b::::2:l:3:f:7\ncell:D31:tvf:5:rowspan:4\ncell:I31:b::2:::l:3:f:7\ncell:A32:b::2:::l:3:f:7\ncell:B32:b::::2:l:3:f:7\ncell:I32:b::2:::l:3:f:7\ncell:A33:b::2:::l:3:f:7\ncell:B33:b::::2:l:3:f:7\ncell:I33:b::2:::l:3:f:7\ncell:A34:b::2:::l:3:f:7\ncell:B34:b::::2:l:3:f:7\ncell:I34:b::2:::l:3:f:7\ncell:A35:b::2:::l:3:f:7\ncell:B35:b:::2:2:l:3:f:7\ncell:C35:b:::2::l:3:f:7\ncell:D35:b:::2::l:3:f:7\ncell:E35:b:::2::l:3:f:7\ncell:F35:b:::2::l:3:f:7\ncell:G35:b:::2::l:3:f:7\ncell:H35:b:::2::l:3:f:7\ncell:I35:b::2:2::l:3:f:7\ncell:B36:b:1:::\ncell:C36:b:1:::\ncell:D36:b:1:::\ncell:E36:b:1:::\ncell:F36:b:1:::\ncell:G36:b:1:::\ncell:H36:b:1:::\ncell:I36:b:1:::\ncol:A:w:26\ncol:B:w:28\ncol:C:w:96\ncol:D:w:203\ncol:E:w:51\ncol:F:w:50\ncol:G:w:58\ncol:H:w:80\ncol:I:w:28\nrow:1:h:14.25\nrow:2:h:18.75\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:15.75\nrow:6:h:15.75\nrow:7:h:15.75\nrow:8:h:15.75\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:15.75\nrow:29:h:15.75\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:9:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\nfont:1:* * Trebuchet MS\nfont:2:normal bold * Trebuchet MS\nfont:3:normal bold 10pt Arial\nfont:4:normal bold 12pt Arial\nfont:5:normal bold 12pt Trebuchet MS\nfont:6:normal bold 14pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 12pt Arial\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:* * * *;vertical-align:top;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00;(#,##0.00)\nvalueformat:3:d-mmm\nvalueformat:4:m/d/yy\nvalueformat:5:text-html\n', + }, + name: "typeii", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "typeii!B2": true, + "typeii!D4": true, + "typeii!C10": true, + "typeii!C11": true, + "typeii!C12": true, + "typeii!C13": true, + "typeii!D9": true, + "typeii!E10": true, + "typeii!E11": true, + "typeii!E12": true, + "typeii!E13": true, + "typeii!F9": true, + "typeii!C16": true, + "typeii!C17": true, + "typeii!C18": true, + "typeii!C19": true, + "typeii!C20": true, + "typeii!C21": true, + "typeii!C22": true, + "typeii!C23": true, + "typeii!C24": true, + "typeii!C25": true, + "typeii!C26": true, + "typeii!C27": true, + "typeii!C28": true, + "typeii!F16": true, + "typeii!F17": true, + "typeii!F18": true, + "typeii!F19": true, + "typeii!F20": true, + "typeii!F21": true, + "typeii!F22": true, + "typeii!F23": true, + "typeii!F24": true, + "typeii!F25": true, + "typeii!F26": true, + "typeii!F27": true, + "typeii!F28": true, + "typeii!G16": true, + "typeii!G17": true, + "typeii!G18": true, + "typeii!G19": true, + "typeii!G20": true, + "typeii!G21": true, + "typeii!G22": true, + "typeii!G23": true, + "typeii!G24": true, + "typeii!G25": true, + "typeii!G26": true, + "typeii!G27": true, + "typeii!G28": true, + "typeii!E35": true, + "typeii!F15": true, + "typeii!G15": true, + "typeii!D6": true, + "typeii!C4": true, + "typeii!C6": true, + "typeii!F4": true, + "typeii!H29": true, + }, + constraints: {}, + }, + }, + }, + + 3003: { + template: "Company-Invoice-1", + templateId: 3003, + footers: [{ name: "Invoice", index: 1, isActive: true }], + logoCell: { + 1: "F4", + }, + signatureCell: { + 1: "C36", + }, + cellMappings: { + 1: { + Heading: "F2", + CompanyName: "B2", + CompanySlogan: "B3", + Date: "G9", + InvoiceNumber: "G10", + From: { + StreetAddress: "B5", + CityStateZip: "B6", + Phone: "B7", + Email: "B8", + }, + BillTo: { + Name: "B11", + CompanyName: "B12", + StreetAddress: "B13", + CityStateZip: "B14", + Phone: "B15", + }, + Items: { + Name: "Items", + Rows: { + start: 18, + end: 29, }, - Content: { + Columns: { Description: "B", Amount: "G", }, }, - TaxRate: "G37", - OtherCharges: "G39", + TaxPercentage: "G31", + OtherCharges: "G33", Notes: { - 1: "B38", - 2: "B39", - 3: "B40", + 1: "B32", + 2: "B33", + 3: "B34", + }, + }, + }, + msc: { + numsheets: 1, + currentid: "sheet3", + currentname: "typeiii", + sheetArr: { + sheet3: { + sheetstr: { + savestr: + "version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:3:cf:2:colspan:3\ncell:C2:t::l:2:f:9\ncell:D2:t::l:2:f:9\ncell:E2:l:1:f:7:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:9\ncell:B3:t:[Company Slogan]:f:4:cf:2:colspan:3\ncell:C3:t::l:2:f:9\ncell:D3:t::l:2:f:9\ncell:B4:f:2:colspan:2\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:2\ncell:F5:l:1:f:6\ncell:G5:l:1:f:10:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:G6:l:1:f:9\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:2\ncell:B8:t:Email\\c:f:1:cf:2:colspan:2\ncell:B9:colspan:2\ncell:F9:t:DATE \\c:l:1:f:6:cf:2\ncell:G9:l:1:f:10:cf:2:ntvf:3\ncell:B10:t:BILL TO\\c:f:5:c:1:bg:3:cf:2:colspan:2\ncell:F10:t:INVOICE # \\c:l:1:f:6:cf:2\ncell:G10:v:1:l:1:f:10:cf:2\ncell:B11:t:[Name]:f:1:cf:2:colspan:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:2\ncell:F12:t: \ncell:B13:t:[Street Address]:f:1:cf:2:colspan:2\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:2\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:6:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C17:t::l:2:f:9\ncell:D17:t::l:2:f:9\ncell:E17:t::l:2:f:9\ncell:F17:t::l:2:f:9\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:6:c:1:bg:3:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C18:t::l:2:f:9\ncell:D18:t::l:2:f:9\ncell:E18:t::l:2:f:9\ncell:F18:t::b::2:::l:1:f:9\ncell:G18:b::1::1:f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C19:t::l:2:f:9\ncell:D19:t::l:2:f:9\ncell:E19:t::l:2:f:9\ncell:F19:t::b::2:::l:1:f:9\ncell:G19:b::1::1:f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C20:t::l:2:f:9\ncell:D20:t::l:2:f:9\ncell:E20:t::l:2:f:9\ncell:F20:t::b::2:::l:1:f:9\ncell:G20:b::1::1:f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C21:t::l:2:f:9\ncell:D21:t::l:2:f:9\ncell:E21:t::l:2:f:9\ncell:F21:t::b::2:::l:1:f:9\ncell:G21:b::1::1:f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C22:t::l:2:f:9\ncell:D22:t::l:2:f:9\ncell:E22:t::l:2:f:9\ncell:F22:t::b::2:::l:1:f:9\ncell:G22:b::1::1:f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:9\ncell:D23:t::l:2:f:9\ncell:E23:t::l:2:f:9\ncell:F23:t::b::2:::l:1:f:9\ncell:G23:b::1::1:f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:9\ncell:D24:t::l:2:f:9\ncell:E24:t::l:2:f:9\ncell:F24:t::b::2:::l:1:f:9\ncell:G24:b::1::1:f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:9\ncell:D25:t::l:2:f:9\ncell:E25:t::l:2:f:9\ncell:F25:t::b::2:::l:1:f:9\ncell:G25:b::1::1:f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:9\ncell:D26:t::l:2:f:9\ncell:E26:t::l:2:f:9\ncell:F26:t::b::2:::l:1:f:9\ncell:G26:b::1::1:f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:9\ncell:D27:t::l:2:f:9\ncell:E27:t::l:2:f:9\ncell:F27:t::b::2:::l:1:f:9\ncell:G27:b::1::1:f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:9\ncell:D28:t::l:2:f:9\ncell:E28:t::l:2:f:9\ncell:F28:t::b::2:::l:1:f:9\ncell:G28:b::1::1:f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:5:rowspan:1\ncell:C29:t::b:::2::l:1:f:9\ncell:D29:t::b:::2::l:1:f:9\ncell:E29:t::b:::2::l:1:f:9\ncell:F29:t::b::2:2::l:1:f:9\ncell:G29:b::1:1:1:f:1:ntvf:1\ncell:B30:b:2::::l:1:f:9\ncell:C30:b:2::::l:1:f:9\ncell:D30:b:2::::l:1:f:9\ncell:E30:b:2::::l:1:f:10\ncell:F30:t:Subtotal:b:2::::l:1:f:10\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:8:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:6:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:9\ncell:D31:t::b:::2::l:1:f:9\ncell:F31:t:Tax Rate:l:1:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3\ncell:C32:t::b:2::::l:1:f:9\ncell:D32:t::b:2::::l:1:f:9\ncell:F32:t:Tax:l:1:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3\ncell:C33:t::l:2:f:9\ncell:D33:t::l:2:f:9\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:9\ncell:D34:t::l:2:f:9\ncell:F34:t:TOTAL:b:2::::l:1:f:6\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:5:ntvf:1\ncell:C36:tvf:4:colspan:5:rowspan:7\ncol:A:w:40\ncol:B:w:232\ncol:C:w:53\ncol:D:w:90\ncol:E:w:54\ncol:F:w:91\ncol:G:w:99\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nsheet:c:7:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 16pt Trebuchet MS\nfont:4:italic normal * Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 28pt Trebuchet MS\nfont:8:normal normal * Trebuchet MS\nfont:9:normal normal 10pt Arial\nfont:10:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", + }, + name: "typeiii", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "typeiii!G9": true, + "typeiii!G10": true, + "typeiii!B2": true, + "typeiii!B3": true, + "typeiii!B4": true, + "typeiii!B5": true, + "typeiii!B6": true, + "typeiii!B7": true, + "typeiii!B8": true, + "typeiii!B9": true, + "typeiii!B11": true, + "typeiii!B12": true, + "typeiii!B13": true, + "typeiii!B14": true, + "typeiii!B15": true, + "typeiii!B18": true, + "typeiii!B19": true, + "typeiii!B20": true, + "typeiii!B21": true, + "typeiii!B22": true, + "typeiii!B23": true, + "typeiii!B24": true, + "typeiii!B25": true, + "typeiii!B26": true, + "typeiii!B27": true, + "typeiii!B28": true, + "typeiii!B29": true, + "typeiii!G18": true, + "typeiii!G19": true, + "typeiii!G20": true, + "typeiii!G21": true, + "typeiii!G22": true, + "typeiii!G23": true, + "typeiii!G24": true, + "typeiii!G25": true, + "typeiii!G26": true, + "typeiii!G27": true, + "typeiii!G28": true, + "typeiii!G29": true, + "typeiii!B32": true, + "typeiii!B33": true, + "typeiii!B34": true, + "typeiii!G31": true, + "typeiii!G33": true, + "typeiii!B37": true, + "typeiii!F10": true, + "typeiii!F9": true, + "typeiii!F4": true, + "typeiii!F2": true, + "typeiii!G30": true, + "typeiii!G34": true, + }, + constraints: {}, + }, + }, + }, + + 3004: { + template: "Company-Invoice-2", + templateId: 3004, + footers: [{ name: "Invoice", index: 1, isActive: true }], + logoCell: { + 1: "F4", + }, + signatureCell: { + 1: "E36", + }, + cellMappings: { + 1: { + Heading: "F2", + CompanyName: "B2", + CompanySlogan: "B3", + Date: "G10", + InvoiceNumber: "G11", + From: { + StreetAddress: "B5", + CityStateZip: "B6", + Phone: "B7", + Email: "B8", + }, + BillTo: { + Name: "B11", + CompanyName: "B12", + StreetAddress: "B13", + CityStateZip: "B14", + Phone: "B15", + }, + Items: { + Name: "Items", + Rows: { + start: 18, + end: 29, + }, + Columns: { + Description: "B", + Hours: "E", + Rate: "F", + }, + }, + TaxPercentage: "G31", + OtherCharges: "G33", + Notes: { + 1: "B32", + 2: "B33", + 3: "B34", + }, + }, + }, + msc: { + numsheets: 1, + currentid: "sheet4", + currentname: "typeiv", + sheetArr: { + sheet4: { + sheetstr: { + savestr: + 'version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:2:cf:2:colspan:3:rowspan:1\ncell:C2:t::l:2:f:7\ncell:D2:t::l:2:f:7\ncell:F2:t:INVOICE:l:1:f:6:c:1:cf:2:colspan:2\ncell:G2:t::l:2:f:7\ncell:B3:t:[Company slogan]:f:3:cf:2:colspan:3:rowspan:1\ncell:C3:t::l:2:f:7\ncell:D3:t::l:2:f:7\ncell:B4:cf:2:colspan:3:rowspan:1\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:F5:l:1:f:5\ncell:G5:l:1:f:8:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:G6:l:1:f:7\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B8:t:Email\\c:f:1:cf:2:colspan:3:rowspan:1\ncell:B9:cf:2:colspan:3:rowspan:1\ncell:B10:t:BILL TO\\c:l:1:f:5:bg:2:cf:2:colspan:2\ncell:F10:t:DATE\\c:l:1:f:5:cf:2\ncell:G10:l:1:f:8:cf:2:ntvf:3\ncell:B11:t:[Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:F11:t:INVOICE # \\c:l:1:f:5:cf:2\ncell:G11:v:1:l:1:f:8:cf:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:J12:tvf:4\ncell:B13:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B16:cf:2:colspan:3:rowspan:1\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:5:bg:2:cf:1:colspan:3:rowspan:1\ncell:C17:t::b:1::1::l:1:f:7:bg:2\ncell:D17:t::b:1::1::l:1:f:7:bg:2\ncell:E17:t:HOURS:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:F17:t:RATE:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C18:t::b:2::::l:1:f:7\ncell:D18:t::b:2:2:::l:1:f:7\ncell:E18:b:1:1::1:f:1:ntvf:1\ncell:F18:b:1:1::1:f:1:ntvf:1\ncell:G18:vtf:t::IF(E18*F18>0,E18*F18,""):b:1:1:::f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C19:t::l:2:f:7\ncell:D19:t::b::2:::l:1:f:7\ncell:E19:b::1::1:f:1:ntvf:1\ncell:F19:b::1::1:f:1:ntvf:1\ncell:G19:vtf:t::IF(E19*F19>0,E19*F19,""):b::1:::f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C20:t::l:2:f:7\ncell:D20:t::b::2:::l:1:f:7\ncell:E20:b::1::1:f:1:ntvf:1\ncell:F20:b::1::1:f:1:ntvf:1\ncell:G20:vtf:t::IF(E20*F20>0,E20*F20,""):b::1:::f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C21:t::l:2:f:7\ncell:D21:t::b::2:::l:1:f:7\ncell:E21:b::1::1:f:1:ntvf:1\ncell:F21:b::1::1:f:1:ntvf:1\ncell:G21:vtf:t::IF(E21*F21>0,E21*F21,""):b::1:::f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C22:t::l:2:f:7\ncell:D22:t::b::2:::l:1:f:7\ncell:E22:b::1::1:f:1:ntvf:1\ncell:F22:b::1::1:f:1:ntvf:1\ncell:G22:vtf:t::IF(E22*F22>0,E22*F22,""):b::1:::f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C23:t::l:2:f:7\ncell:D23:t::b::2:::l:1:f:7\ncell:E23:b::1::1:f:1:ntvf:1\ncell:F23:b::1::1:f:1:ntvf:1\ncell:G23:vtf:t::IF(E23*F23>0,E23*F23,""):b::1:::f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C24:t::l:2:f:7\ncell:D24:t::b::2:::l:1:f:7\ncell:E24:b::1::1:f:1:ntvf:1\ncell:F24:b::1::1:f:1:ntvf:1\ncell:G24:vtf:t::IF(E24*F24>0,E24*F24,""):b::1:::f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C25:t::l:2:f:7\ncell:D25:t::b::2:::l:1:f:7\ncell:E25:b::1::1:f:1:ntvf:1\ncell:F25:b::1::1:f:1:ntvf:1\ncell:G25:vtf:t::IF(E25*F25>0,E25*F25,""):b::1:::f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C26:t::l:2:f:7\ncell:D26:t::b::2:::l:1:f:7\ncell:E26:b::1::1:f:1:ntvf:1\ncell:F26:b::1::1:f:1:ntvf:1\ncell:G26:vtf:t::IF(E26*F26>0,E26*F26,""):b::1:::f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C27:t::l:2:f:7\ncell:D27:t::b::2:::l:1:f:7\ncell:E27:b::1::1:f:1:ntvf:1\ncell:F27:b::1::1:f:1:ntvf:1\ncell:G27:vtf:t::IF(E27*F27>0,E27*F27,""):b::1:::f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C28:t::l:2:f:7\ncell:D28:t::b::2:::l:1:f:7\ncell:E28:b::1::1:f:1:ntvf:1\ncell:F28:b::1::1:f:1:ntvf:1\ncell:G28:vtf:t::IF(E28*F28>0,E28*F28,""):b::1:::f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:3:rowspan:1\ncell:C29:t::b:::2::l:1:f:7\ncell:D29:t::b::2:2::l:1:f:7\ncell:E29:b::1:1:1:f:1:ntvf:1\ncell:F29:b::1:1:1:f:1:ntvf:1\ncell:G29:vtf:t::IF(E29*F29>0,E29*F29,""):b::1:::f:1:ntvf:1\ncell:B30:b:2::::l:1:f:8:cf:1:colspan:3:rowspan:1\ncell:C30:t::b:2::::l:1:f:7\ncell:D30:t::b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:8\ncell:F30:t:Subtotal:b:1::::f:1\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:1:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:5:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:7\ncell:D31:t::b:::2::l:1:f:7\ncell:F31:t:Tax Rate:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3:rowspan:1\ncell:C32:t::b:2::::l:1:f:7\ncell:D32:t::b:2::::l:1:f:7\ncell:F32:t:Tax:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3:rowspan:1\ncell:C33:t::l:2:f:7\ncell:D33:t::l:2:f:7\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:7\ncell:D34:t::l:2:f:7\ncell:F34:t:TOTAL:b:1::::l:1:f:4\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:4:ntvf:1\ncell:B35:b:2::::l:1:f:7\ncell:C35:b:2::::l:1:f:7\ncell:D35:b:2::::l:1:f:7\ncell:C36:tvf:4\ncell:E36:colspan:3:rowspan:4\ncol:A:w:40\ncol:B:w:194\ncol:C:w:128\ncol:D:w:60\ncol:E:w:65\ncol:F:w:95\ncol:G:w:90\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:10:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncolor:1:rgb(0,0,0)\ncolor:2:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 16pt Trebuchet MS\nfont:3:italic normal * Trebuchet MS\nfont:4:normal bold * Trebuchet MS\nfont:5:normal bold 10pt Trebuchet MS\nfont:6:normal bold 28pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', + }, + name: "typeiv", + hidden: "0", + }, + }, + EditableCells: { + allow: true, + cells: { + "typeiv!G9": true, + "typeiv!G10": true, + "typeiv!B2": true, + "typeiv!B3": true, + "typeiv!B4": true, + "typeiv!B5": true, + "typeiv!B6": true, + "typeiv!B7": true, + "typeiv!B8": true, + "typeiv!B11": true, + "typeiv!B12": true, + "typeiv!B13": true, + "typeiv!B14": true, + "typeiv!B15": true, + "typeiv!B16": true, + "typeiv!B18": true, + "typeiv!B19": true, + "typeiv!B20": true, + "typeiv!B21": true, + "typeiv!B22": true, + "typeiv!B23": true, + "typeiv!B24": true, + "typeiv!B25": true, + "typeiv!B26": true, + "typeiv!B27": true, + "typeiv!B28": true, + "typeiv!B29": true, + "typeiv!E18": true, + "typeiv!E19": true, + "typeiv!E20": true, + "typeiv!E21": true, + "typeiv!E22": true, + "typeiv!E23": true, + "typeiv!E24": true, + "typeiv!E25": true, + "typeiv!E26": true, + "typeiv!E27": true, + "typeiv!E28": true, + "typeiv!E29": true, + "typeiv!F18": true, + "typeiv!F19": true, + "typeiv!F2": true, + "typeiv!F20": true, + "typeiv!F21": true, + "typeiv!F22": true, + "typeiv!F23": true, + "typeiv!F24": true, + "typeiv!F25": true, + "typeiv!F26": true, + "typeiv!F27": true, + "typeiv!F28": true, + "typeiv!F29": true, + "typeiv!B32": true, + "typeiv!B33": true, + "typeiv!B34": true, + "typeiv!G31": true, + "typeiv!G33": true, + "typeiv!B37": true, + "typeiv!E17": true, + "typeiv!F17": true, + "typeiv!G11": true, + "typeiv!F11": true, + "typeiv!F10": true, + "typeiv!F4": true, + "typeiv!G30": true, + "typeiv!G34": true, }, + constraints: {}, }, }, }, diff --git a/src/utils/dynamicFormManager.ts b/src/utils/dynamicFormManager.ts index 3ed49f0..afff0d4 100644 --- a/src/utils/dynamicFormManager.ts +++ b/src/utils/dynamicFormManager.ts @@ -1,4 +1,4 @@ -import { TemplateData } from "../templates"; +import { TemplateData, ItemsConfig } from "../templates"; export interface DynamicFormField { label: string; @@ -64,17 +64,20 @@ export class DynamicFormManager { const sections: DynamicFormSection[] = []; Object.entries(cellMappings).forEach(([key, value]) => { - if (key === "Items") { - // Special handling for Items - const itemsConfig = value as any; + if (key === "Items" && this.isItemsConfig(value)) { + // Handle new Items structure with Name, Rows, and Columns + const itemsConfig = value as ItemsConfig; sections.push({ - title: itemsConfig.name || "Items", + title: itemsConfig.Name, fields: [], isItems: true, itemsConfig: { - name: itemsConfig.name || "Items", - range: itemsConfig.Range || { start: 1, end: 10 }, - content: itemsConfig.Content || {}, + name: itemsConfig.Name, + range: { + start: itemsConfig.Rows.start, + end: itemsConfig.Rows.end, + }, + content: itemsConfig.Columns, }, }); } else if (typeof value === "string") { @@ -123,6 +126,24 @@ export class DynamicFormManager { return sections; } + /** + * Type guard to check if an object is an ItemsConfig + * @param value The value to check + * @returns True if the value is an ItemsConfig + */ + static isItemsConfig(value: any): value is ItemsConfig { + return ( + value && + typeof value === "object" && + typeof value.Name === "string" && + value.Rows && + typeof value.Rows.start === "number" && + typeof value.Rows.end === "number" && + value.Columns && + typeof value.Columns === "object" + ); + } + /** * Initializes form data based on form sections * @param sections The form sections diff --git a/src/utils/templateInitializer.ts b/src/utils/templateInitializer.ts index 67880d4..548a316 100644 --- a/src/utils/templateInitializer.ts +++ b/src/utils/templateInitializer.ts @@ -1,5 +1,4 @@ import { DATA, TemplateData } from "../templates"; -import { TemplateMetadata } from "../components/Storage/LocalStorage"; import { TemplateManager } from "./templateManager"; /** @@ -134,13 +133,11 @@ export class TemplateInitializer { } /** - * Get template metadata + * Get template data */ - static getTemplateMetadata(templateId: number): TemplateMetadata | null { + static getTemplateData(templateId: number): TemplateData | null { const template = this.getTemplate(templateId); - if (!template) return null; - - return TemplateManager.extractMetadata(template); + return template || null; } /** diff --git a/src/utils/templateManager.ts b/src/utils/templateManager.ts index 86b2359..8656401 100644 --- a/src/utils/templateManager.ts +++ b/src/utils/templateManager.ts @@ -127,23 +127,8 @@ export class TemplateManager { static generateDefaultCellMappings( templateId: number ): TemplateMetadata["cellMappings"] { - // Default mappings based on footer index (0 = first footer, 1 = second footer, etc.) + // Default mappings based on footer index (1 = first footer, 2 = second footer, etc.) const defaultMappings: TemplateMetadata["cellMappings"] = { - 0: { - "Company Name": "B8", - "Street Address": "B9", - City: "B10", - Phone: "B11", - Email: "B12", - "Invoice Number": "B5", - Date: "F4", - "Due Date": "G4", - "Customer Name": "B15", - "Customer Company": "B16", - "Customer Address": "B17", - "Customer Phone": "B19", - "Customer Email": "B20", - }, 1: { "Company Name": "B8", "Street Address": "B9", From 2dc79bca2eed95316015a30905dc0aae311986f6 Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Sat, 30 Aug 2025 10:40:27 +0530 Subject: [PATCH 6/9] removed template initializer --- src/components/Files/Files.tsx | 3 - src/pages/FilesPage.tsx | 22 ++-- src/pages/Home.tsx | 68 +--------- src/utils/templateInitializer.ts | 208 ------------------------------- src/utils/templateManager.ts | 151 ---------------------- 5 files changed, 9 insertions(+), 443 deletions(-) delete mode 100644 src/utils/templateInitializer.ts delete mode 100644 src/utils/templateManager.ts diff --git a/src/components/Files/Files.tsx b/src/components/Files/Files.tsx index c630db3..4f3e4b2 100644 --- a/src/components/Files/Files.tsx +++ b/src/components/Files/Files.tsx @@ -122,9 +122,6 @@ const Files: React.FC<{ return template ? template.template : `Template ${templateId}`; }; - const getFileTemplateInfo = (fileData: any) => { - return fileData.templateMetadata || null; - }; // Edit local file const editFile = async (key: string) => { diff --git a/src/pages/FilesPage.tsx b/src/pages/FilesPage.tsx index b22e7cf..71b5a10 100644 --- a/src/pages/FilesPage.tsx +++ b/src/pages/FilesPage.tsx @@ -40,8 +40,6 @@ import * as AppGeneral from "../components/socialcalc/index"; import "./FilesPage.css"; import { useHistory } from "react-router-dom"; import { File } from "../components/Storage/LocalStorage"; -import { TemplateInitializer } from "../utils/templateInitializer"; - const FilesPage: React.FC = () => { const { isDarkMode, toggleDarkMode } = useTheme(); const { @@ -84,11 +82,6 @@ const FilesPage: React.FC = () => { } }, []); - // Template helper functions - const getAvailableTemplates = () => { - return TemplateInitializer.getAllTemplates(); - }; - const getTemplateMetadata = (templateId: number) => { return tempMeta.find(meta => meta.template_id === templateId); }; @@ -107,11 +100,11 @@ const FilesPage: React.FC = () => { // Get categorized templates const getCategorizedTemplates = () => { - const templates = getAvailableTemplates(); + const templates = tempMeta; const categorized = { - web: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.templateId)?.name || t.template) === 'web'), - mobile: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.templateId)?.name || t.template) === 'mobile'), - tablet: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.templateId)?.name || t.template) === 'tablet'), + web: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.template_id)?.name || t.name) === 'web'), + mobile: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.template_id)?.name || t.name) === 'mobile'), + tablet: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.template_id)?.name || t.name) === 'tablet'), }; return categorized; }; @@ -188,14 +181,15 @@ const FilesPage: React.FC = () => { return; } - const templateData = TemplateInitializer.getTemplateData(templateId); + const templateData = DATA[templateId]; if (!templateData) { setToastMessage("Template not found"); setShowToast(true); return; } - const mscContent = TemplateInitializer.createMSCContent(templateId); + const mscContent = templateData.msc; + const jsonMsc = JSON.stringify(mscContent); if (!mscContent) { setToastMessage("Error creating template content"); setShowToast(true); @@ -210,7 +204,7 @@ const FilesPage: React.FC = () => { const newFile = new File( now, now, - encodeURIComponent(mscContent), // mscContent is already a JSON string + encodeURIComponent(jsonMsc), // mscContent is already a JSON string fileName, activeFooterIndex, templateId, diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 063a3dc..3e12f9e 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -22,7 +22,6 @@ import { IonSegmentButton, IonFab, IonFabButton, - IonSpinner, isPlatform, } from "@ionic/react"; import { APP_NAME, DATA } from "../templates"; @@ -74,11 +73,9 @@ const Home: React.FC = () => { const { selectedFile, billType, store, updateSelectedFile, updateBillType, activeTemplateData, updateActiveTemplateData } = useInvoice(); const history = useHistory(); - const { fileName } = useParams<{ fileName?: string }>(); const [fileNotFound, setFileNotFound] = useState(false); const [templateNotFound, setTemplateNotFound] = useState(false); - const [isInitializing, setIsInitializing] = useState(true); const [showMenu, setShowMenu] = useState(false); const [showToast, setShowToast] = useState(false); @@ -279,7 +276,6 @@ const Home: React.FC = () => { }; const initializeApp = async () => { - setIsInitializing(true); try { // Initialize template system first @@ -290,15 +286,10 @@ const initializeApp = async () => { // Prioritize URL parameter over context to ensure fresh state let fileToLoad=selectedFile; - if (!selectedFile || selectedFile.trim() === "") { - fileToLoad = fileName; - updateSelectedFile(fileName); - } // If no file is specified, redirect to files page if (!fileToLoad || fileToLoad === "") { // console.log("No file specified, redirecting to files"); - setIsInitializing(false); history.push("/app/files"); return; } @@ -309,7 +300,6 @@ const initializeApp = async () => { if (!fileExists) { console.log(`File "${fileToLoad}" not found`); setFileNotFound(true); - setIsInitializing(false); return; } @@ -325,7 +315,6 @@ const initializeApp = async () => { console.error(`Template ${templateId} not found in templates library`); setTemplateNotFound(true); setFileNotFound(false); - setIsInitializing(false); return; } @@ -370,7 +359,6 @@ const initializeApp = async () => { // Activate footer after initialization setTimeout(() => { activateFooter(fileData.billType); - setIsInitializing(false); // Set loading to false after complete initialization }, 500); }, 100); console.log("success"); @@ -382,7 +370,6 @@ const initializeApp = async () => { // On error, show file not found setFileNotFound(true); setTemplateNotFound(false); - setIsInitializing(false); } }; @@ -390,11 +377,6 @@ const initializeApp = async () => { initializeApp(); }, [selectedFile]); // Only depend on selectedFile to prevent loops with selectedFile updates - useEffect(() => { - if (fileName) { - updateSelectedFile(fileName); - } - }, [fileName]); const [autoSaveTimer, setAutoSaveTimer] = useState( null @@ -716,7 +698,7 @@ const initializeApp = async () => { lineHeight: "1.5", maxWidth: "400px" }}> - {fileName ? `The file "${fileName}" doesn't exist in your storage.` : "The requested file couldn't be found."} + {selectedFile ? `The file "${selectedFile}" doesn't exist in your storage.` : "The requested file couldn't be found."}

{
) : ( -
- {/* Loading overlay */} - {isInitializing && ( -
- -

- Initializing App -

-

- Please wait while we load your invoice template and prepare the editor... -

-
- )} - - {/* SocialCalc container - always rendered */}
-
)} {/* Toast for save notifications */} diff --git a/src/utils/templateInitializer.ts b/src/utils/templateInitializer.ts deleted file mode 100644 index 548a316..0000000 --- a/src/utils/templateInitializer.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { DATA, TemplateData } from "../templates"; -import { TemplateManager } from "./templateManager"; - -/** - * Template Initialization System - * Handles the setup and migration to the new multi-template architecture - */ -export class TemplateInitializer { - /** - * Initialize the application with multi-template support - */ - static async initializeApp(): Promise { - console.log("🚀 Initializing Multi-Template Architecture..."); - - try { - // Validate template data - this.validateTemplateData(); - - // Setup default template metadata - this.setupDefaultMetadata(); - - // Initialize template registry - await this.initializeTemplateRegistry(); - - console.log("✅ Multi-Template Architecture initialized successfully"); - } catch (error) { - console.error( - "❌ Failed to initialize multi-template architecture:", - error - ); - throw error; - } - } - - /** - * Validate template data structure - */ - private static validateTemplateData(): void { - console.log("🔍 Validating template data..."); - - for (const [id, template] of Object.entries(DATA)) { - if (!template.template || !template.templateId) { - throw new Error(`Invalid template data for template ${id}`); - } - - if (!template.msc || !template.msc.sheetArr) { - throw new Error(`Missing MSC data for template ${id}`); - } - - console.log(`✓ Template ${id}: ${template.template} - Valid`); - } - } - - /** - * Setup default metadata for existing templates - */ - private static setupDefaultMetadata(): void { - console.log("⚙️ Setting up default metadata..."); - - for (const [id, template] of Object.entries(DATA)) { - // Ensure footers exist - if (!template.footers || template.footers.length === 0) { - template.footers = [ - { name: template.template, index: 1, isActive: true }, - ]; - } - - // Ensure cellMappings exist - if (!template.cellMappings) { - template.cellMappings = TemplateManager.generateDefaultCellMappings( - template.templateId - ); - } - - // Ensure logoCell and signatureCell exist - if (template.logoCell === undefined) { - template.logoCell = null; - } - if (template.signatureCell === undefined) { - template.signatureCell = null; - } - - console.log(`✓ Metadata setup complete for template ${id}`); - } - } - - /** - * Initialize template registry in localStorage - */ - private static async initializeTemplateRegistry(): Promise { - console.log("📝 Initializing template registry..."); - - const registry = { - version: "2.0.0", - templates: Object.keys(DATA).map((id) => { - const template = DATA[parseInt(id)]; - return { - id: template.templateId, - name: template.template, - version: "1.0.0", - created: new Date().toISOString(), - modified: new Date().toISOString(), - }; - }), - initialized: new Date().toISOString(), - }; - - try { - if (typeof window !== "undefined" && window.localStorage) { - localStorage.setItem("template_registry", JSON.stringify(registry)); - console.log("✓ Template registry saved to localStorage"); - } - } catch (error) { - console.warn( - "⚠️ Could not save template registry to localStorage:", - error - ); - } - } - - /** - * Get template by ID - */ - static getTemplate(templateId: number): TemplateData | null { - return DATA[templateId] || null; - } - - /** - * Get all available templates - */ - static getAllTemplates(): TemplateData[] { - return Object.values(DATA); - } - - /** - * Get template data - */ - static getTemplateData(templateId: number): TemplateData | null { - const template = this.getTemplate(templateId); - return template || null; - } - - /** - * Create MSC content for a template - */ - static createMSCContent(templateId: number): string | null { - const template = this.getTemplate(templateId); - if (!template) return null; - - try { - // Convert the MSC object to a string format - return JSON.stringify(template.msc); - } catch (error) { - console.error( - `Error creating MSC content for template ${templateId}:`, - error - ); - return null; - } - } - - /** - * Check if the app has been initialized with the new architecture - */ - static async isInitialized(): Promise { - try { - if (typeof window !== "undefined" && window.localStorage) { - const registry = localStorage.getItem("template_registry"); - if (registry) { - const parsed = JSON.parse(registry); - return parsed.version === "2.0.0"; - } - } - return false; - } catch (error) { - return false; - } - } - - /** - * Migration utility for existing files - */ - static async migrateExistingFiles(): Promise { - console.log("🔄 Starting migration of existing files..."); - - // This would be implemented to migrate existing files to the new structure - // For now, we'll just log that it should be implemented - console.log( - "⚠️ File migration not yet implemented - manual migration required" - ); - } - - /** - * Reset the template system (for development/testing) - */ - static async reset(): Promise { - console.log("🔄 Resetting template system..."); - - try { - if (typeof window !== "undefined" && window.localStorage) { - localStorage.removeItem("template_registry"); - console.log("✓ Template registry cleared"); - } - } catch (error) { - console.warn("⚠️ Could not clear template registry:", error); - } - } -} diff --git a/src/utils/templateManager.ts b/src/utils/templateManager.ts deleted file mode 100644 index 8656401..0000000 --- a/src/utils/templateManager.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { TemplateData } from "../templates"; -import { TemplateMetadata } from "../components/Storage/LocalStorage"; - -/** - * Template Manager Utility - * Handles multi-template operations and metadata management - */ -export class TemplateManager { - /** - * Extract template metadata from template data - */ - static extractMetadata(templateData: TemplateData): TemplateMetadata { - return { - template: templateData.template, - templateId: templateData.templateId, - footers: templateData.footers, - logoCell: templateData.logoCell, - signatureCell: templateData.signatureCell, - cellMappings: templateData.cellMappings, - }; - } - - /** - * Create a complete template metadata object with MSC content - */ - static createTemplateWithMSC( - templateData: TemplateData, - mscContent: string - ): { - metadata: TemplateMetadata; - mscContent: string; - } { - return { - metadata: this.extractMetadata(templateData), - mscContent, - }; - } - - /** - * Get template-specific storage key - */ - static getTemplateStorageKey(templateId: number, baseName: string): string { - return `template_${templateId}_${baseName}`; - } - - /** - * Parse template ID from storage key - */ - static parseTemplateIdFromKey(storageKey: string): number | null { - const match = storageKey.match(/^template_(\d+)_/); - return match ? parseInt(match[1], 10) : null; - } - - /** - * Validate template metadata - */ - static validateMetadata(metadata: TemplateMetadata): boolean { - return ( - typeof metadata.template === "string" && - typeof metadata.templateId === "number" && - Array.isArray(metadata.footers) && - metadata.footers.every( - (footer) => - typeof footer.name === "string" && - typeof footer.index === "number" && - typeof footer.isActive === "boolean" - ) - ); - } - - /** - * Merge cell mappings from different sources - */ - static mergeCellMappings( - existing: TemplateMetadata["cellMappings"], - newMappings: TemplateMetadata["cellMappings"] - ): TemplateMetadata["cellMappings"] { - const merged = { ...existing }; - - for (const [headingName, cellMappings] of Object.entries(newMappings)) { - if (merged[headingName]) { - merged[headingName] = { ...merged[headingName], ...cellMappings }; - } else { - merged[headingName] = { ...cellMappings }; - } - } - - return merged; - } - - /** - * Filter files by template ID - */ - static filterFilesByTemplate( - files: Record, - templateId: number - ): Record { - const filtered: Record = {}; - - for (const [fileName, fileData] of Object.entries(files)) { - if (fileData.templateMetadata?.templateId === templateId) { - filtered[fileName] = fileData; - } - } - - return filtered; - } - - /** - * Get unique template IDs from files - */ - static getUniqueTemplateIds(files: Record): number[] { - const templateIds = new Set(); - - for (const fileData of Object.values(files)) { - if (fileData.templateMetadata?.templateId) { - templateIds.add(fileData.templateMetadata.templateId); - } - } - - return Array.from(templateIds).sort(); - } - - /** - * Generate default cell mappings for a template - */ - static generateDefaultCellMappings( - templateId: number - ): TemplateMetadata["cellMappings"] { - // Default mappings based on footer index (1 = first footer, 2 = second footer, etc.) - const defaultMappings: TemplateMetadata["cellMappings"] = { - 1: { - "Company Name": "B8", - "Street Address": "B9", - City: "B10", - Phone: "B11", - Email: "B12", - "Invoice Number": "B5", - Date: "F4", - "Due Date": "G4", - "Customer Name": "B15", - "Customer Company": "B16", - "Customer Address": "B17", - "Customer Phone": "B19", - "Customer Email": "B20", - }, - }; - - return defaultMappings; - } -} From 24a43a8140e03a47725ffafe3118a7f86da5379d Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Sun, 31 Aug 2025 02:32:51 +0530 Subject: [PATCH 7/9] multitemplate offine v1 --- .github/bug-fix/extra-bottom-spacing-fix.md | 123 ++ LOGO_SIGNATURE_REFACTOR.md | 44 + android/app/build.gradle | 10 +- src/App.css | 8 +- src/App.tsx | 35 +- src/a.txt | 1 + src/components/DynamicFormDemo.tsx | 63 - src/components/DynamicInvoiceForm.tsx | 12 +- src/components/FileMenu/FileOptions.tsx | 253 +++- src/components/Files/Files.tsx | 207 +-- src/components/FilesComponent.tsx | 0 src/components/InvoiceForm.tsx | 12 +- src/components/Menu/Menu.tsx | 9 +- src/components/OfflineFallback.tsx | 82 - src/components/PWADemo.tsx | 4 +- src/components/TemplateFiles.tsx | 265 ---- src/components/socialcalc/modules/device.js | 102 +- src/components/socialcalc/modules/init.js | 22 +- .../socialcalc/modules/listeners.js | 4 +- src/contexts/InvoiceContext.tsx | 12 +- src/pages/FilesPage.tsx | 1314 ++++++++++------- src/pages/Home.css | 58 +- src/pages/Home.tsx | 857 +++++++---- src/pages/SettingsPage.tsx | 68 +- src/services/exportAllAsPdf.ts | 3 +- src/services/exportAllSheetsAsPdf.ts | 36 +- src/services/exportAsCsv.ts | 3 +- src/services/exportAsPdf.ts | 25 +- src/templates-meta.ts | 42 +- src/templates.ts | 76 +- src/utils/settings.ts | 42 + 31 files changed, 2083 insertions(+), 1709 deletions(-) create mode 100644 .github/bug-fix/extra-bottom-spacing-fix.md create mode 100644 LOGO_SIGNATURE_REFACTOR.md create mode 100644 src/a.txt delete mode 100644 src/components/DynamicFormDemo.tsx delete mode 100644 src/components/FilesComponent.tsx delete mode 100644 src/components/OfflineFallback.tsx delete mode 100644 src/components/TemplateFiles.tsx create mode 100644 src/utils/settings.ts diff --git a/.github/bug-fix/extra-bottom-spacing-fix.md b/.github/bug-fix/extra-bottom-spacing-fix.md new file mode 100644 index 0000000..ff3c235 --- /dev/null +++ b/.github/bug-fix/extra-bottom-spacing-fix.md @@ -0,0 +1,123 @@ +# Bug Fix: Extra Bottom Spacing in Spreadsheet + +## Issue Description + +### Problem + +The spreadsheet component (SocialCalc) was displaying an unwanted ~100px margin/padding at the bottom of the page after initialization. This extra space was visible below the spreadsheet grid, creating a poor user experience on mobile devices. + +### Symptoms + +- Extra white space (~100px) below the spreadsheet +- Spreadsheet not utilizing full available viewport height +- Poor mobile layout experience + +### Root Cause + +The issue was caused by SocialCalc's automatic height calculation in the `DoOnResize()` function, which was: + +1. Using viewport-based calculations that included space for elements not present in our mobile layout +2. Not properly accounting for the actual available content area height +3. Applying default spacing values that weren't appropriate for our container setup + +## Solution + +### Files Modified + +#### 1. `/src/pages/Home.css` + +**Added CSS fixes to remove extra spacing:** + +```css +/* SocialCalc specific fixes */ +#te_griddiv { + margin-bottom: 0 !important; + padding-bottom: 0 !important; +} + +/* Force SocialCalc container to not have extra bottom space */ +.SocialCalc-spreadsheet { + margin-bottom: 0 !important; + padding-bottom: 0 !important; +} + +/* Ensure the spreadsheet control fills available space properly */ +#tableeditor > div { + margin-bottom: 0 !important; + padding-bottom: 0 !important; +} +``` + +#### 2. `/src/components/socialcalc/modules/init.js` + +**Enhanced the initialization function with proper height calculation:** + +```javascript +// Calculate proper height for the spreadsheet +let ele = document.getElementById("te_griddiv"); +if (ele) { + // Get the available height from the container + const container = document.getElementById("container"); + const ionContent = document.querySelector("ion-content"); + const ionHeader = document.querySelector("ion-header"); + + if (container && ionContent && ionHeader) { + const headerHeight = ionHeader.offsetHeight || 0; + const viewportHeight = window.innerHeight; + const availableHeight = viewportHeight - headerHeight; + + // Set a more precise height for mobile + ele.style.height = availableHeight + "px"; + ele.style.marginBottom = "0px"; + ele.style.paddingBottom = "0px"; + } +} +``` + +### Technical Details + +1. **CSS Approach**: Used `!important` declarations to override SocialCalc's default styling that was adding unwanted spacing. + +2. **JavaScript Approach**: + + - Calculate actual available height by subtracting header height from viewport height + - Explicitly set the grid container height to use all available space + - Remove any bottom margins/padding programmatically + +3. **Targeting**: Focused on the `#te_griddiv` element, which is the main SocialCalc grid container where the spacing issue originated. + +## Testing + +### Before Fix + +- Spreadsheet had ~100px extra space at bottom +- Poor mobile user experience +- Wasted screen real estate + +### After Fix + +- Spreadsheet extends to full available height +- No extra spacing at bottom +- Improved mobile layout +- Better utilization of screen space + +## Impact + +- **User Experience**: Significantly improved mobile layout +- **Performance**: No performance impact +- **Compatibility**: Maintains compatibility with existing functionality +- **Responsive Design**: Better mobile responsiveness + +## Related Issues + +This fix addresses layout issues specifically related to: + +- Mobile viewport calculations +- SocialCalc integration with Ionic framework +- Container height management in single-page applications + +--- + +**Date**: August 30, 2025 +**Fixed By**: Assistant +**Tested On**: Mobile browsers, various viewport sizes diff --git a/LOGO_SIGNATURE_REFACTOR.md b/LOGO_SIGNATURE_REFACTOR.md new file mode 100644 index 0000000..15c219e --- /dev/null +++ b/LOGO_SIGNATURE_REFACTOR.md @@ -0,0 +1,44 @@ +## Logo and Signature Coordinates Refactoring + +### Summary + +Updated FileOptions.tsx to use activeTemplateData from the InvoiceContext instead of deprecated getLogoCoordinates() and getSignatureCoordinates() functions. + +### Changes Made + +1. **FileOptions.tsx Updates:** + + - Added `activeTemplateData` to the destructured context values from `useInvoice()` + - Updated `handleSelectLogo()` to use `activeTemplateData.logoCell[billType]` instead of `AppGeneral.getLogoCoordinates()` + - Updated `handleRemoveLogo()` to use `activeTemplateData.logoCell[billType]` instead of `AppGeneral.getLogoCoordinates()` + - Updated `handleSelectSignature()` to use `activeTemplateData.signatureCell[billType]` instead of `AppGeneral.getSignatureCoordinates()` + - Updated `handleRemoveSignature()` to use `activeTemplateData.signatureCell[billType]` instead of `AppGeneral.getSignatureCoordinates()` + - Added proper error handling for cases where activeTemplateData is null or coordinates are unavailable + - Implemented support for both string and object-based coordinate definitions in template data + +2. **device.js Module Cleanup:** + - Removed deprecated `getLogoCoordinates()` function + - Removed deprecated `getSignatureCoordinates()` function + - Kept only `getDeviceType()` function as it's still needed + +### Benefits + +- **Better Architecture:** Logo and signature coordinates are now sourced directly from template metadata instead of hardcoded device-specific mappings +- **Dynamic Positioning:** Coordinates can vary per template and bill type, providing more flexibility +- **Cleaner Code:** Removed deprecated functions and their hardcoded coordinate mappings +- **Type Safety:** Better TypeScript support with proper template data interfaces +- **Error Handling:** Added comprehensive error messages for missing template data or coordinates + +### Template Data Structure + +The system now expects coordinates in the activeTemplateData object: + +```typescript +{ + logoCell: string | { [billType: number]: string }, + signatureCell: string | { [billType: number]: string }, + // ... other template properties +} +``` + +This allows for either simple string coordinates (same for all bill types) or object-based coordinates (different per bill type). diff --git a/android/app/build.gradle b/android/app/build.gradle index 221c72b..ce1d7f7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,8 @@ android { } } signingConfigs { - if (keystorePropertiesFile.exists()) { + release { + if (keystorePropertiesFile.exists()) { storeFile file(keystoreProperties['RELEASE_STORE_FILE']) storePassword keystoreProperties['RELEASE_STORE_PASSWORD'] keyAlias keystoreProperties['RELEASE_KEY_ALIAS'] @@ -38,14 +39,17 @@ android { keyPassword RELEASE_KEY_PASSWORD } } + } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - // Add signing config to release build - signingConfig signingConfigs.release + // Add signing config to release build only if keystore is available + if (keystorePropertiesFile.exists() || project.hasProperty('RELEASE_STORE_FILE')) { + signingConfig signingConfigs.release + } } } } diff --git a/src/App.css b/src/App.css index 560db2e..b2175a0 100644 --- a/src/App.css +++ b/src/App.css @@ -530,7 +530,7 @@ ion-title { ion-header { --min-height: 40px; } - + ion-toolbar { --min-height: 40px; --padding-top: 2px; @@ -553,7 +553,7 @@ ion-title { ion-header { --min-height: 36px; } - + ion-toolbar { --min-height: 36px; --padding-start: 8px; @@ -803,7 +803,7 @@ ion-title { ion-header { --min-height: 40px; } - + ion-toolbar { --min-height: 40px; --padding-top: 2px; @@ -826,7 +826,7 @@ ion-title { ion-header { --min-height: 36px; } - + ion-toolbar { --min-height: 36px; --padding-start: 8px; diff --git a/src/App.tsx b/src/App.tsx index 4f8c571..88e1d1d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,16 @@ -import { - IonApp, - IonRouterOutlet, - setupIonicReact, -} from "@ionic/react"; +import { IonApp, IonRouterOutlet, setupIonicReact } from "@ionic/react"; import { IonReactRouter } from "@ionic/react-router"; import { Route, Redirect } from "react-router-dom"; import Home from "./pages/Home"; import FilesPage from "./pages/FilesPage"; import SettingsPage from "./pages/SettingsPage"; import LandingPage from "./pages/LandingPage"; -import DynamicFormDemo from "./components/DynamicFormDemo"; import { ThemeProvider, useTheme } from "./contexts/ThemeContext"; import { InvoiceProvider } from "./contexts/InvoiceContext"; import PWAUpdatePrompt from "./components/PWAUpdatePrompt"; import OfflineIndicator from "./components/OfflineIndicator"; import { usePWA } from "./hooks/usePWA"; import { isNewUser } from "./utils/helper"; -import { TemplateInitializer } from "./utils/templateInitializer"; -import { useEffect } from "react"; - /* Core CSS required for Ionic components to work properly */ import "@ionic/react/css/core.css"; @@ -46,33 +38,13 @@ const AppContent: React.FC = () => { const { isOnline } = usePWA(); const showLandingPage = isNewUser(); - // Initialize multi-template architecture - useEffect(() => { - const initializeApp = async () => { - try { - const isInitialized = await TemplateInitializer.isInitialized(); - if (!isInitialized) { - await TemplateInitializer.initializeApp(); - } - } catch (error) { - console.error('Failed to initialize template system:', error); - } - }; - - initializeApp(); - }, []); - return ( - {showLandingPage ? ( - - ) : ( - - )} + {showLandingPage ? : } {!isOnline && } @@ -89,9 +61,6 @@ const AppContent: React.FC = () => { - - - diff --git a/src/a.txt b/src/a.txt new file mode 100644 index 0000000..9675108 --- /dev/null +++ b/src/a.txt @@ -0,0 +1 @@ +Logo \ No newline at end of file diff --git a/src/components/DynamicFormDemo.tsx b/src/components/DynamicFormDemo.tsx deleted file mode 100644 index 40ee357..0000000 --- a/src/components/DynamicFormDemo.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useState } from "react"; -import { - IonContent, - IonHeader, - IonPage, - IonTitle, - IonToolbar, - IonButton, - IonCard, - IonCardContent, - IonCardHeader, - IonCardTitle, -} from "@ionic/react"; -import DynamicInvoiceForm from "../components/DynamicInvoiceForm"; - -const DynamicFormDemo: React.FC = () => { - const [showForm, setShowForm] = useState(false); - - return ( - - - - Dynamic Form Demo - - - -
- - - Dynamic Invoice Form System - - -

- This demo showcases the dynamic form generation system that creates forms based on: -

-
    -
  • Template cell mappings
  • -
  • Active footer indices
  • -
  • Field type detection
  • -
  • Dynamic validation
  • -
- - setShowForm(true)} - style={{ marginTop: "20px" }} - > - Open Dynamic Form - -
-
-
- - setShowForm(false)} - /> -
-
- ); -}; - -export default DynamicFormDemo; diff --git a/src/components/DynamicInvoiceForm.tsx b/src/components/DynamicInvoiceForm.tsx index 3fa2548..9b51db2 100644 --- a/src/components/DynamicInvoiceForm.tsx +++ b/src/components/DynamicInvoiceForm.tsx @@ -22,14 +22,12 @@ import { IonToast, IonItemDivider, IonTextarea, - IonFab, - IonFabButton, IonSelect, IonSelectOption, IonChip, } from "@ionic/react"; -import { close, save, add, trash, layers } from "ionicons/icons"; -import { DATA, TemplateData } from "../templates"; +import { close, save, trash, layers } from "ionicons/icons"; +import { TemplateData } from "../templates"; import { useInvoice } from "../contexts/InvoiceContext"; import { addInvoiceData, @@ -134,8 +132,9 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose onClose(); }, 1500); } catch (error) { - console.error("Error saving invoice data:", error); - showToastMessage("Failed to save invoice data", "danger"); + setToastMessage("Failed to save invoice data. Please try again."); + setToastColor("danger"); + setShowToast(true); } }; @@ -147,7 +146,6 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose setFormData(initData); showToastMessage("Form data cleared successfully!", "success"); } catch (error) { - console.error("Error clearing form data:", error); showToastMessage("Failed to clear form data", "danger"); } }; diff --git a/src/components/FileMenu/FileOptions.tsx b/src/components/FileMenu/FileOptions.tsx index f553906..1127642 100644 --- a/src/components/FileMenu/FileOptions.tsx +++ b/src/components/FileMenu/FileOptions.tsx @@ -62,6 +62,8 @@ interface FileOptionsProps { setShowActionsPopover: (show: boolean) => void; showColorModal: boolean; setShowColorPicker: (show: boolean) => void; + onSave?: () => Promise; + isAutoSaveEnabled?: boolean; } const FileOptions: React.FC = ({ @@ -69,6 +71,8 @@ const FileOptions: React.FC = ({ setShowActionsPopover, showColorModal, setShowColorPicker, + onSave, + isAutoSaveEnabled = false, }) => { const { isDarkMode } = useTheme(); const [showToast, setShowToast] = useState(false); @@ -97,11 +101,23 @@ const FileOptions: React.FC = ({ selectedFile, store, billType, + activeTemplateData, updateSelectedFile, updateBillType, resetToDefaults, } = useInvoice(); + // Helper function to trigger save if autosave is enabled + const triggerAutoSave = async () => { + if (isAutoSaveEnabled && onSave) { + try { + await onSave(); + } catch (error) { + console.error("Auto-save failed:", error); + } + } + }; + // Load saved logos and signatures on component mount useEffect(() => { loadSavedLogos(); @@ -129,16 +145,49 @@ const FileOptions: React.FC = ({ setShowLogoModal(true); }; - const handleSelectLogo = (logo: { + const handleSelectLogo = async (logo: { id: string; name: string; data: string; }) => { - const logoCoordinates = AppGeneral.getLogoCoordinates(); - AppGeneral.addLogo(logoCoordinates, logo.data); - setToastMessage(`Logo applied successfully!`); - setShowToast(true); - setShowLogoModal(false); + if (!activeTemplateData) { + setToastMessage("No active template data available"); + setShowToast(true); + return; + } + + const logoCell = activeTemplateData.logoCell; + const currentSheetId = activeTemplateData.msc.currentid; + let logoCoordinate: string; + + if (typeof logoCell === "string") { + logoCoordinate = logoCell; + } else if (logoCell && logoCell[currentSheetId]) { + logoCoordinate = logoCell[currentSheetId]; + } else { + setToastMessage("Logo position not available for current sheet"); + setShowToast(true); + return; + } + + try { + // Create coordinates object for the current sheet + const logoCoordinates = { + [currentSheetId]: logoCoordinate, + }; + + AppGeneral.addLogo(logoCoordinates, logo.data); + setToastMessage(`Logo applied successfully!`); + setShowToast(true); + setShowLogoModal(false); + + // Trigger auto-save if enabled + await triggerAutoSave(); + } catch (error) { + console.error("Error applying logo:", error); + setToastMessage("Failed to apply logo"); + setShowToast(true); + } }; // Local storage functions for signatures @@ -230,60 +279,6 @@ const FileOptions: React.FC = ({ setShowSaveAsAlert(true); }; - const doSave = async () => { - try { - setToastMessage("Saving..."); - setShowToast(true); - - const content = AppGeneral.getSpreadsheetContent(); - - if (selectedFile === "default") { - // Save as new file - const now = new Date().toISOString(); - const filename = "Untitled-" + formatDateForFilename(new Date()); - const file = new File( - now, - now, - encodeURIComponent(content), - filename, - 1 - ); - await store._saveFile(file); - updateSelectedFile(filename); - setToastMessage("File saved as " + filename); - } else { - // Update existing file - const existingFile = await store._getFile(selectedFile); - const now = new Date().toISOString(); - const updatedFile = new File( - existingFile.created, - now, - encodeURIComponent(content), - selectedFile, - existingFile.billType, - existingFile.isEncrypted - ); - await store._saveFile(updatedFile); - setToastMessage("File saved successfully!"); - } - setShowToast(true); - } catch (error) { - console.error("Error saving file:", error); - - if (isQuotaExceededError(error)) { - setToastMessage(getQuotaExceededMessage("saving file")); - } else { - setToastMessage("Failed to save file. Please try again."); - } - setShowToast(true); - } - }; - - const handleSave = () => { - setShowActionsPopover(false); - doSave(); - }; - const handleNewFileClick = async () => { try { setShowActionsPopover(false); @@ -366,20 +361,88 @@ const FileOptions: React.FC = ({ return "A1"; }; - const handleRemoveLogo = () => { + const handleRemoveLogo = async () => { setShowActionsPopover(false); - const logoCoordinates = AppGeneral.getLogoCoordinates(); - AppGeneral.removeLogo(logoCoordinates); - setToastMessage("Logo removed successfully!"); - setShowToast(true); + + if (!activeTemplateData) { + setToastMessage("No active template data available"); + setShowToast(true); + return; + } + + const logoCell = activeTemplateData.logoCell; + const currentSheetId = activeTemplateData.msc.currentid; + let logoCoordinate: string; + + if (typeof logoCell === "string") { + logoCoordinate = logoCell; + } else if (logoCell && logoCell[currentSheetId]) { + logoCoordinate = logoCell[currentSheetId]; + } else { + setToastMessage("Logo position not available for current sheet"); + setShowToast(true); + return; + } + + try { + // Create coordinates object for the current sheet + const logoCoordinates = { + [currentSheetId]: logoCoordinate, + }; + + AppGeneral.removeLogo(logoCoordinates); + setToastMessage("Logo removed successfully!"); + setShowToast(true); + + // Trigger auto-save if enabled + await triggerAutoSave(); + } catch (error) { + console.error("Error removing logo:", error); + setToastMessage("Failed to remove logo"); + setShowToast(true); + } }; - const handleRemoveSignature = () => { + const handleRemoveSignature = async () => { setShowActionsPopover(false); - const signatureCoordinates = AppGeneral.getSignatureCoordinates(); - AppGeneral.removeLogo(signatureCoordinates); - setToastMessage("Signature removed successfully!"); - setShowToast(true); + + if (!activeTemplateData) { + setToastMessage("No active template data available"); + setShowToast(true); + return; + } + + const signatureCell = activeTemplateData.signatureCell; + const currentSheetId = activeTemplateData.msc.currentid; + let signatureCoordinate: string; + + if (typeof signatureCell === "string") { + signatureCoordinate = signatureCell; + } else if (signatureCell && signatureCell[currentSheetId]) { + signatureCoordinate = signatureCell[currentSheetId]; + } else { + setToastMessage("Signature position not available for current sheet"); + setShowToast(true); + return; + } + + try { + // Create coordinates object for the current sheet + const signatureCoordinates = { + [currentSheetId]: signatureCoordinate, + }; + + AppGeneral.removeLogo(signatureCoordinates); + setToastMessage("Signature removed successfully!"); + setShowToast(true); + + // Trigger auto-save if enabled + await triggerAutoSave(); + } catch (error) { + console.error("Error removing signature:", error); + setToastMessage("Failed to remove signature"); + setShowToast(true); + } }; // Signature management functions @@ -390,16 +453,49 @@ const FileOptions: React.FC = ({ setShowSignatureModal(true); }; - const handleSelectSignature = (signature: { + const handleSelectSignature = async (signature: { id: string; name: string; data: string; }) => { - const signatureCoordinates = AppGeneral.getSignatureCoordinates(); - AppGeneral.addLogo(signatureCoordinates, signature.data); - setToastMessage(`Signature applied successfully!`); - setShowToast(true); - setShowSignatureModal(false); + if (!activeTemplateData) { + setToastMessage("No active template data available"); + setShowToast(true); + return; + } + + const signatureCell = activeTemplateData.signatureCell; + const currentSheetId = activeTemplateData.msc.currentid; + let signatureCoordinate: string; + + if (typeof signatureCell === "string") { + signatureCoordinate = signatureCell; + } else if (signatureCell && signatureCell[currentSheetId]) { + signatureCoordinate = signatureCell[currentSheetId]; + } else { + setToastMessage("Signature position not available for current sheet"); + setShowToast(true); + return; + } + + try { + // Create coordinates object for the current sheet + const signatureCoordinates = { + [currentSheetId]: signatureCoordinate, + }; + + AppGeneral.addLogo(signatureCoordinates, signature.data); + setToastMessage(`Signature applied successfully!`); + setShowToast(true); + setShowSignatureModal(false); + + // Trigger auto-save if enabled + await triggerAutoSave(); + } catch (error) { + console.error("Error applying signature:", error); + setToastMessage("Failed to apply signature"); + setShowToast(true); + } }; return ( @@ -420,11 +516,6 @@ const FileOptions: React.FC = ({ New - - - Save - - Save As diff --git a/src/components/Files/Files.tsx b/src/components/Files/Files.tsx index 4f3e4b2..539c79c 100644 --- a/src/components/Files/Files.tsx +++ b/src/components/Files/Files.tsx @@ -68,8 +68,7 @@ import { isQuotaExceededError, getQuotaExceededMessage, } from "../../utils/helper"; -import { TemplateManager } from "../../utils/templateManager"; -import { TemplateInitializer } from "../../utils/templateInitializer"; +import { tempMeta } from "../../templates-meta"; const Files: React.FC<{ store: Local; @@ -77,8 +76,12 @@ const Files: React.FC<{ updateSelectedFile: Function; updateBillType: Function; }> = (props) => { - const { selectedFile, updateSelectedFile, activeTemplateData, updateActiveTemplateData } = - useInvoice(); + const { + selectedFile, + updateSelectedFile, + activeTemplateData, + updateActiveTemplateData, + } = useInvoice(); const { isDarkMode } = useTheme(); const history = useHistory(); @@ -114,47 +117,27 @@ const Files: React.FC<{ // Template helper functions const getAvailableTemplates = () => { - return TemplateInitializer.getAllTemplates(); + // map tempMeta.template_id and tempMeta.tempate_name with templateId and template resp + return tempMeta.map((template) => ({ + templateId: template.template_id, + template: template.name, + ImageUri: template.ImageUri, + })); }; const getTemplateInfo = (templateId: number) => { - const template = TemplateInitializer.getTemplate(templateId); + const template = DATA[templateId]; return template ? template.template : `Template ${templateId}`; }; - // Edit local file - const editFile = async (key: string) => { - try { - console.log("Opening file:", key); - - - // Clear any existing context state to prevent conflicts with old state - const fileData = await props.store._getFile(key); - const templateId = fileData?.templateId || 1; - const templateData = DATA[templateId]; - if (!templateData) { - setToastMessage("Template not found"); - setShowToast(true); - return; - } - updateSelectedFile(key); - updateActiveTemplateData(templateData); - - - // Optional: Show loading feedback - setToastMessage(`Opening ${key}...`); - setShowToast(true); - // Small delay to ensure context is cleared before navigation - setTimeout(() => { - history.push(`/app/editor/${encodeURIComponent(key)}`); - }, 10); - - } catch (error) { - console.error("Error in editFile:", error); - setToastMessage("Failed to navigate to editor"); - setShowToast(true); - } + const editFile = (key: string) => { + // Create a temporary anchor element to navigate + setTimeout(() => { + const link = document.createElement("a"); + link.href = `/app/editor/${key}`; + link.click(); + }, 50); }; // Delete file @@ -381,14 +364,15 @@ const Files: React.FC<{ // Get the existing file data const fileData = await props.store._getFile(currentRenameKey); - // Create a new file with the new name + // Create a new file with the new name, preserving all original metadata including templateId const renamedFile = new LocalFile( fileData.created, // Keep the original creation date new Date().toISOString(), // Use ISO string for modified date fileData.content, newFileName, fileData.billType, - fileData.isPasswordProtected, + fileData.templateId || fileData.billType, // Preserve templateId, fallback to billType for backward compatibility + fileData.isEncrypted || false, fileData.password ); @@ -414,8 +398,6 @@ const Files: React.FC<{ setRenameFileName(""); setShowRenameAlert(false); } catch (error) { - console.error("Error renaming file:", error); - // Check if the error is due to storage quota exceeded if (isQuotaExceededError(error)) { setToastMessage(getQuotaExceededMessage("renaming files")); @@ -442,36 +424,32 @@ const Files: React.FC<{ if (fileSource === "local") { const localFiles = await props.store._getAllFiles(); - const filesArray = Object.keys(localFiles) - .map((key) => { - const fileData = localFiles[key]; - - // Ensure dates are properly converted - handle both ISO strings and Date.toString() formats - let createdDate = fileData.created; - let modifiedDate = fileData.modified; - - // If the date looks like a Date.toString() format, try to parse it - // Date.toString() typically looks like "Mon Jul 06 2025 10:30:00 GMT+0000 (UTC)" - if (typeof createdDate === "string" && createdDate.includes("GMT")) { - createdDate = new Date(createdDate).toISOString(); - } - if ( - typeof modifiedDate === "string" && - modifiedDate.includes("GMT") - ) { - modifiedDate = new Date(modifiedDate).toISOString(); - } - - return { - key, - name: key, - date: modifiedDate, // For backward compatibility - dateCreated: createdDate, - dateModified: modifiedDate, - type: "local", - templateMetadata: fileData.templateMetadata || null, - }; - }); + const filesArray = Object.keys(localFiles).map((key) => { + const fileData = localFiles[key]; + + // Ensure dates are properly converted - handle both ISO strings and Date.toString() formats + let createdDate = fileData.created; + let modifiedDate = fileData.modified; + + // If the date looks like a Date.toString() format, try to parse it + // Date.toString() typically looks like "Mon Jul 06 2025 10:30:00 GMT+0000 (UTC)" + if (typeof createdDate === "string" && createdDate.includes("GMT")) { + createdDate = new Date(createdDate).toISOString(); + } + if (typeof modifiedDate === "string" && modifiedDate.includes("GMT")) { + modifiedDate = new Date(modifiedDate).toISOString(); + } + + return { + key, + name: key, + date: modifiedDate, // For backward compatibility + dateCreated: createdDate, + dateModified: modifiedDate, + type: "local", + templateMetadata: fileData.templateMetadata || null, + }; + }); // Filter by template if a specific template is selected let filteredFiles = filesArray; @@ -488,7 +466,9 @@ const Files: React.FC<{ const emptyMessage = searchQuery.trim() ? `No files found matching "${searchQuery}"` : selectedTemplateFilter !== "all" - ? `No files found for ${getTemplateInfo(selectedTemplateFilter as number)}` + ? `No files found for ${getTemplateInfo( + selectedTemplateFilter as number + )}` : "No local files found"; content = ( @@ -521,12 +501,25 @@ const Files: React.FC<{ className="file-icon document-icon" /> -
+

{file.name}

{file.templateMetadata && ( - + - {file.templateMetadata.template} + + {file.templateMetadata.template} + )}
@@ -610,12 +603,25 @@ const Files: React.FC<{ className="file-icon document-icon" /> -
+

{file.name}

{file.templateMetadata && ( - + - {file.templateMetadata.template} + + {file.templateMetadata.template} + )}
@@ -673,7 +679,14 @@ const Files: React.FC<{ useEffect(() => { renderFileList(); // eslint-disable-next-line - }, [props.file, fileSource, searchQuery, sortBy, serverFilesLoading, selectedTemplateFilter]); + }, [ + props.file, + fileSource, + searchQuery, + sortBy, + serverFilesLoading, + selectedTemplateFilter, + ]); // Check screen size useEffect(() => { @@ -682,8 +695,8 @@ const Files: React.FC<{ }; checkScreenSize(); - window.addEventListener('resize', checkScreenSize); - return () => window.removeEventListener('resize', checkScreenSize); + window.addEventListener("resize", checkScreenSize); + return () => window.removeEventListener("resize", checkScreenSize); }, []); // Reset sort option when switching file sources to ensure compatibility @@ -730,7 +743,7 @@ const Files: React.FC<{ debounce={300} style={{ flex: "2", minWidth: "200px" }} /> - + {/* Template Filter */}
{!isSmallScreen && ( setSelectedTemplateFilter(e.detail.value)} + onIonChange={(e) => + setSelectedTemplateFilter(e.detail.value) + } style={{ flex: "1", "--placeholder-color": "var(--ion-color-medium)", @@ -759,7 +777,10 @@ const Files: React.FC<{ > All Templates {getAvailableTemplates().map((template) => ( - + {template.template} ))} @@ -769,7 +790,9 @@ const Files: React.FC<{ setSelectedTemplateFilter(e.detail.value)} + onIonChange={(e) => + setSelectedTemplateFilter(e.detail.value) + } style={{ flex: "1", "--placeholder-color": "var(--ion-color-medium)", @@ -781,7 +804,10 @@ const Files: React.FC<{ > All Templates {getAvailableTemplates().map((template) => ( - + {template.template} ))} @@ -800,7 +826,10 @@ const Files: React.FC<{ > {!isSmallScreen && ( = ({ isOpen, onClose }) => { setTimeout(() => { onClose(); }, 1500); - } catch (error) { - console.error("Error saving invoice data:", error); - showToastMessage("Failed to save invoice data", "danger"); + } catch (error) { + setToastMessage("Failed to save invoice data. Please try again."); + setToastColor("danger"); + setShowToast(true); } }; @@ -257,8 +258,9 @@ const InvoiceForm: React.FC = ({ isOpen, onClose }) => { }); showToastMessage("Invoice data cleared successfully!", "success"); } catch (error) { - console.error("Error clearing invoice data:", error); - showToastMessage("Failed to clear invoice data", "danger"); + setToastMessage("Failed to clear invoice data"); + setToastColor("danger"); + setShowToast(true); } }; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index f00667b..74b6186 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -103,7 +103,6 @@ const Menu: React.FC<{ setToastMessage("Print job sent successfully!"); setShowToast1(true); } catch (error) { - console.error("Print error:", error); setToastMessage( "Failed to print. Please check if a printer is available." ); @@ -126,7 +125,6 @@ const Menu: React.FC<{ // Get the current HTML content from the spreadsheet const htmlContent = AppGeneral.getCurrentHTMLContent(); - console.log(htmlContent); if (!htmlContent || htmlContent.trim() === "") { setToastMessage("No content available to export as PDF"); @@ -179,8 +177,7 @@ const Menu: React.FC<{ setToastMessage(`PDF generated and ready to share!`); setShowToast1(true); } catch (shareError) { - console.log("Error sharing PDF:", shareError); - // Fallback: still generate PDF normally + // Error sharing PDF, fallback: still generate PDF normally await exportHTMLAsPDF(htmlContent, { filename: pdfFilename, format: "a4", @@ -197,8 +194,7 @@ const Menu: React.FC<{ }; reader.readAsDataURL(pdfBlob as Blob); } catch (error) { - console.error("Error processing PDF for sharing:", error); - // Fallback to normal PDF generation + // Error processing PDF for sharing, fallback to normal PDF generation await exportHTMLAsPDF(htmlContent, { filename: pdfFilename, format: "a4", @@ -230,7 +226,6 @@ const Menu: React.FC<{ setShowToast1(true); } } catch (error) { - console.error("Error generating PDF:", error); setToastMessage("Failed to generate PDF. Please try again."); setShowToast1(true); } finally { diff --git a/src/components/OfflineFallback.tsx b/src/components/OfflineFallback.tsx deleted file mode 100644 index ec97100..0000000 --- a/src/components/OfflineFallback.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { - IonButton, - IonCard, - IonCardContent, - IonCardHeader, - IonCardTitle, - IonContent, - IonIcon, - IonPage -} from '@ionic/react'; -import { cloudOfflineOutline, refreshOutline, homeOutline } from 'ionicons/icons'; - -interface OfflineFallbackProps { - onRetry?: () => void; - onHome?: () => void; -} - -const OfflineFallback: React.FC = ({ onRetry, onHome }) => { - return ( - - -
- - - - - You're Offline - - -

- This content isn't available offline. Please check your internet - connection and try again. -

- -
- {onRetry && ( - - - Try Again - - )} - - {onHome && ( - - - Go Home - - )} -
-
-
- -
-

- Tip: Install this app to your home screen for better offline access! -

-
-
-
-
- ); -}; - -export default OfflineFallback; \ No newline at end of file diff --git a/src/components/PWADemo.tsx b/src/components/PWADemo.tsx index 8d95a1f..1077b49 100644 --- a/src/components/PWADemo.tsx +++ b/src/components/PWADemo.tsx @@ -53,7 +53,7 @@ const PWADemo: React.FC = () => { const data = await getAllInvoices(); setOfflineData(data); } catch (error) { - console.error("Error loading offline data:", error); + // Error loading offline data } }; @@ -85,7 +85,7 @@ const PWADemo: React.FC = () => { // icon: '/pwa-192x192.png' // }); // } - console.log("Notification functionality disabled"); + // Notification functionality disabled }; return ( diff --git a/src/components/TemplateFiles.tsx b/src/components/TemplateFiles.tsx deleted file mode 100644 index 06bcc2c..0000000 --- a/src/components/TemplateFiles.tsx +++ /dev/null @@ -1,265 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - IonCard, - IonCardContent, - IonCardHeader, - IonCardTitle, - IonButton, - IonSegment, - IonSegmentButton, - IonLabel, - IonList, - IonItem, - IonIcon, - IonBadge, - IonChip, - IonToast, -} from '@ionic/react'; -import { documentText, folder, download, create, trash } from 'ionicons/icons'; -import { Local, File } from './Storage/LocalStorage'; -import { TemplateManager } from '../utils/templateManager'; -import { TemplateInitializer } from '../utils/templateInitializer'; - -interface TemplateFilesProps { - onFileSelect?: (fileName: string, templateId: number) => void; - onFileCreate?: (templateId: number) => void; -} - -/** - * Enhanced Files Component with Multi-Template Support - * Demonstrates the new template isolation architecture - */ -const TemplateFiles: React.FC = ({ onFileSelect, onFileCreate }) => { - const [selectedTemplate, setSelectedTemplate] = useState('all'); - const [files, setFiles] = useState>({}); - const [availableTemplates, setAvailableTemplates] = useState([]); - const [loading, setLoading] = useState(true); - const [toastMessage, setToastMessage] = useState(''); - - const local = new Local(); - - useEffect(() => { - loadFiles(); - loadAvailableTemplates(); - }, []); - - const loadFiles = async () => { - try { - setLoading(true); - const allFiles = await local._getAllFiles(); - setFiles(allFiles); - } catch (error) { - console.error('Error loading files:', error); - setToastMessage('Error loading files'); - } finally { - setLoading(false); - } - }; - - const loadAvailableTemplates = async () => { - try { - const allFiles = await local._getAllFiles(); - const templateIds = TemplateManager.getUniqueTemplateIds(allFiles); - setAvailableTemplates(templateIds); - } catch (error) { - console.error('Error loading templates:', error); - } - }; - - const getFilteredFiles = () => { - if (selectedTemplate === 'all') { - return files; - } - return TemplateManager.filterFilesByTemplate(files, selectedTemplate as number); - }; - - const getTemplateInfo = (templateId: number) => { - const templateData = TemplateInitializer.getTemplateData(templateId); - return templateData ? templateData.template : `Template ${templateId}`; - }; - - const handleFileCreate = async (templateId: number) => { - try { - const templateData = TemplateInitializer.getTemplateData(templateId); - if (!templateData) { - setToastMessage('Template not found'); - return; - } - - const mscContent = TemplateInitializer.createMSCContent(templateId); - if (!mscContent) { - setToastMessage('Error creating template content'); - return; - } - - const fileName = `invoice_${Date.now()}.msc`; - const newFile = new File( - new Date().toISOString(), - new Date().toISOString(), - mscContent, - fileName, - templateId, - templateId, - false - ); - - await local._saveFile(newFile); - await loadFiles(); - setToastMessage(`File created with ${templateData.template}`); - - if (onFileCreate) { - onFileCreate(templateId); - } - } catch (error) { - console.error('Error creating file:', error); - setToastMessage('Error creating file'); - } - }; - - const handleFileDelete = async (fileName: string) => { - try { - await local._deleteFile(fileName); - await loadFiles(); - setToastMessage('File deleted successfully'); - } catch (error) { - console.error('Error deleting file:', error); - setToastMessage('Error deleting file'); - } - }; - - const getFileTemplateInfo = (fileData: any): number | null => { - return fileData.templateId || null; - }; - - const filteredFiles = getFilteredFiles(); - const fileCount = Object.keys(filteredFiles).length; - - return ( -
- - - Multi-Template File Manager - - - {/* Template Filter */} - setSelectedTemplate(e.detail.value as number | 'all')} - > - - All Templates - {Object.keys(files).length} - - {availableTemplates.map(templateId => ( - - {getTemplateInfo(templateId)} - - {Object.keys(TemplateManager.filterFilesByTemplate(files, templateId)).length} - - - ))} - - - {/* Create New File Buttons */} -
- {TemplateInitializer.getAllTemplates().map(template => ( - handleFileCreate(template.templateId)} - > - - New {template.template} - - ))} -
- - {/* Files List */} - {loading ? ( -
Loading files...
- ) : fileCount === 0 ? ( -
- -
No files found for selected template
-
- ) : ( - - {Object.entries(filteredFiles).map(([fileName, fileData]) => { - const templateId = getFileTemplateInfo(fileData); - const templateData = templateId ? TemplateInitializer.getTemplateData(templateId) : null; - return ( - - -
-
{fileName}
-
- Created: {new Date(fileData.created).toLocaleDateString()} - {fileData.modified !== fileData.created && ( - <> • Modified: {new Date(fileData.modified).toLocaleDateString()} - )} -
- {templateData && ( -
- - {templateData.template} - - {templateData.footers.length > 0 && ( - - {templateData.footers.length} footer(s) - - )} - {fileData.isEncrypted && ( - - Encrypted - - )} -
- )} -
- onFileSelect && onFileSelect(fileName, templateId || 1)} - > - - - handleFileDelete(fileName)} - > - - -
- ); - })} -
- )} - - {/* Template Isolation Info */} - {selectedTemplate !== 'all' && ( -
-
- Template Isolation Active -
-
- Showing only files for {getTemplateInfo(selectedTemplate as number)}. - Files are isolated by template to prevent interference between different invoice types. -
-
- )} -
-
- - setToastMessage('')} - message={toastMessage} - duration={3000} - position="bottom" - /> -
- ); -}; - -export default TemplateFiles; diff --git a/src/components/socialcalc/modules/device.js b/src/components/socialcalc/modules/device.js index c8dd806..ca51f23 100644 --- a/src/components/socialcalc/modules/device.js +++ b/src/components/socialcalc/modules/device.js @@ -1,4 +1,4 @@ -// Device detection and coordinate utilities +// Device detection utilities let SocialCalc; // Ensure SocialCalc is loaded from the global scope @@ -20,103 +20,3 @@ export function getDeviceType() { if (navigator.userAgent.match(/Android/)) device = "Android"; return device; } - -export function getLogoCoordinates() { - const deviceType = getDeviceType(); - console.log("=== GET LOGO COORDINATES ==="); - console.log("Detected device type:", deviceType); - - // Import the LOGO configuration (you'll need to import this) - // For now, returning a basic structure - you should import from app-data-new.ts - const LOGO = { - iPad: { - sheet1: "F4", - sheet2: "F4", - sheet3: "F4", - sheet4: "F4", - }, - iPhone: { - sheet1: "F5", - sheet2: "F7", - sheet3: "F8", - sheet4: null, - sheet5: null, - }, - iPod: { - sheet1: "F5", - sheet2: "F7", - sheet3: "F8", - sheet4: null, - sheet5: null, - }, - Android: { - sheet1: "F5", - sheet2: "F7", - sheet3: "F8", - sheet4: null, - sheet5: null, - }, - default: { - sheet1: "F4", - sheet2: "F4", - sheet3: "F4", - sheet4: "F4", - }, - }; - - const coordinates = LOGO[deviceType] || LOGO.default; - console.log("Selected coordinates:", coordinates); - console.log("=== END GET LOGO COORDINATES ==="); - - return coordinates; -} - -export function getSignatureCoordinates() { - const deviceType = getDeviceType(); - console.log("=== GET SIGNATURE COORDINATES ==="); - console.log("Detected device type:", deviceType); - - // Import the SIGNATURE configuration (you'll need to import this) - // For now, returning a basic structure - you should import from app-data-new.ts - const SIGNATURE = { - iPad: { - sheet1: null, - sheet2: null, - sheet3: null, - sheet4: null, - }, - iPhone: { - sheet1: null, - sheet2: null, - sheet3: null, - sheet4: null, - sheet5: null, - }, - iPod: { - sheet1: null, - sheet2: null, - sheet3: null, - sheet4: null, - sheet5: null, - }, - Android: { - sheet1: null, - sheet2: null, - sheet3: null, - sheet4: null, - sheet5: null, - }, - default: { - sheet1: "D31", - sheet2: "D31", - sheet3: "C36", - sheet4: "C36", - }, - }; - - const coordinates = SIGNATURE[deviceType] || SIGNATURE.default; - console.log("Selected coordinates:", coordinates); - console.log("=== END GET SIGNATURE COORDINATES ==="); - - return coordinates; -} diff --git a/src/components/socialcalc/modules/init.js b/src/components/socialcalc/modules/init.js index c5fb04f..6db1fb2 100644 --- a/src/components/socialcalc/modules/init.js +++ b/src/components/socialcalc/modules/init.js @@ -30,8 +30,26 @@ export function initializeApp(data) { workbookcontrol.InitializeWorkBookControl(); // alert("app: "+JSON.stringify(data)); SocialCalc.WorkBookControlLoad(data); - // Fixed height setting - this could be problematic for mobile + + // Calculate proper height for the spreadsheet let ele = document.getElementById("te_griddiv"); - // ele.style.height = "1600px"; + if (ele) { + // Get the available height from the container + const container = document.getElementById("container"); + const ionContent = document.querySelector("ion-content"); + const ionHeader = document.querySelector("ion-header"); + + if (container && ionContent && ionHeader) { + const headerHeight = ionHeader.offsetHeight || 0; + const viewportHeight = window.innerHeight; + const availableHeight = viewportHeight - headerHeight; + + // Set a more precise height for mobile + ele.style.height = availableHeight + "px"; + ele.style.marginBottom = "0px"; + ele.style.paddingBottom = "0px"; + } + } + spreadsheet.DoOnResize(); } diff --git a/src/components/socialcalc/modules/listeners.js b/src/components/socialcalc/modules/listeners.js index b28951c..bb26b20 100644 --- a/src/components/socialcalc/modules/listeners.js +++ b/src/components/socialcalc/modules/listeners.js @@ -16,8 +16,8 @@ export function setupCellChangeListener(callback) { // Add safety check if (!control || !control.workbook || !control.workbook.spreadsheet) { - console.warn("Spreadsheet not initialized yet. Retrying in 100ms..."); - setTimeout(() => setupCellChangeListener(callback), 100); + // Spreadsheet not initialized yet. Retrying in 100ms... + setTimeout(setupListener, 100); return () => {}; // Return empty cleanup function } diff --git a/src/contexts/InvoiceContext.tsx b/src/contexts/InvoiceContext.tsx index cfc2495..ce6e413 100644 --- a/src/contexts/InvoiceContext.tsx +++ b/src/contexts/InvoiceContext.tsx @@ -36,7 +36,7 @@ interface InvoiceProviderProps { export const InvoiceProvider: React.FC = ({ children, }) => { - const [selectedFile, setSelectedFile] = useState("default"); + const [selectedFile, setSelectedFile] = useState("file_not_found"); const [billType, setBillType] = useState(1); const [activeTemplateData, setActiveTemplateData] = useState(null); const [store] = useState(() => new Local()); @@ -64,7 +64,7 @@ export const InvoiceProvider: React.FC = ({ } } } catch (error) { - console.warn("Failed to load invoice state from localStorage:", error); + // Failed to load invoice state from localStorage } }, []); @@ -73,7 +73,7 @@ export const InvoiceProvider: React.FC = ({ try { localStorage.setItem("stark-invoice-selected-file", selectedFile); } catch (error) { - console.warn("Failed to save selected file to localStorage:", error); + // Failed to save selected file to localStorage } }, [selectedFile]); @@ -81,7 +81,7 @@ export const InvoiceProvider: React.FC = ({ try { localStorage.setItem("stark-invoice-bill-type", billType.toString()); } catch (error) { - console.warn("Failed to save bill type to localStorage:", error); + // Failed to save bill type to localStorage } }, [billType]); @@ -93,7 +93,7 @@ export const InvoiceProvider: React.FC = ({ localStorage.removeItem("stark-invoice-active-template-id"); } } catch (error) { - console.warn("Failed to save active template id to localStorage:", error); + // Failed to save active template id to localStorage } }, [activeTemplateData]); @@ -110,7 +110,7 @@ export const InvoiceProvider: React.FC = ({ }; const resetToDefaults = () => { - setSelectedFile("default"); + setSelectedFile("File_Not_found"); setBillType(1); setActiveTemplateData(null); }; diff --git a/src/pages/FilesPage.tsx b/src/pages/FilesPage.tsx index 71b5a10..2f452cb 100644 --- a/src/pages/FilesPage.tsx +++ b/src/pages/FilesPage.tsx @@ -11,11 +11,8 @@ import { IonIcon, IonButtons, IonModal, - IonFab, - IonFabButton, IonSegment, IonSegmentButton, - IonLabel, IonText, } from "@ionic/react"; import { @@ -42,23 +39,22 @@ import { useHistory } from "react-router-dom"; import { File } from "../components/Storage/LocalStorage"; const FilesPage: React.FC = () => { const { isDarkMode, toggleDarkMode } = useTheme(); - const { - selectedFile, - billType, - store, - updateSelectedFile, - updateBillType, - } = useInvoice(); + const { selectedFile, store, updateSelectedFile, updateBillType } = + useInvoice(); const history = useHistory(); const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); - const [selectedTemplateForFile, setSelectedTemplateForFile] = useState(null); + const [selectedTemplateForFile, setSelectedTemplateForFile] = useState< + number | null + >(null); const [newFileName, setNewFileName] = useState(""); const [showTemplateModal, setShowTemplateModal] = useState(false); const [isSmallScreen, setIsSmallScreen] = useState(false); - const [templateFilter, setTemplateFilter] = useState<"all" | "web" | "mobile" | "tablet">("all"); + const [templateFilter, setTemplateFilter] = useState< + "all" | "web" | "mobile" | "tablet" + >("all"); const [device] = useState(AppGeneral.getDeviceType()); @@ -69,42 +65,70 @@ const FilesPage: React.FC = () => { }; checkScreenSize(); - window.addEventListener('resize', checkScreenSize); - return () => window.removeEventListener('resize', checkScreenSize); + window.addEventListener("resize", checkScreenSize); + return () => window.removeEventListener("resize", checkScreenSize); }, []); // Clear selected file when navigating to files page to prevent conflicts useEffect(() => { - // Clear the selected file to prevent infinite loops when navigating back - if (selectedFile && selectedFile !== "") { - console.log("Clearing selected file when navigating to files page"); - updateSelectedFile(""); - } + updateSelectedFile(""); }, []); const getTemplateMetadata = (templateId: number) => { - return tempMeta.find(meta => meta.template_id === templateId); + return tempMeta.find((meta) => meta.template_id === templateId); }; // Categorize templates based on their names - const categorizeTemplate = (templateName: string) => { + const categorizeTemplate = (templateName: string | undefined) => { + if (!templateName) return "web"; const name = templateName.toLowerCase(); - if (name.includes('mobile')) { - return 'mobile'; - } else if (name.includes('tablet')) { - return 'tablet'; + if (name.includes("mobile")) { + return "mobile"; + } else if (name.includes("tablet")) { + return "tablet"; } else { - return 'web'; + return "web"; } }; + const getAvailableTemplates = () => { + // map tempMeta.template_id and tempMeta.tempate_name with templateId and template resp + return tempMeta.map((template) => { + const extra = DATA[template.template_id]; + return { + templateId: template.template_id, + template: template.name, + ImageUri: template.ImageUri, + footers: extra?.footers || [], + ...extra, + }; + }); + }; + + const getTemplateInfo = (templateId: number) => { + const template = DATA[templateId]; + return template ? template.template : `Template ${templateId}`; + }; + // Get categorized templates const getCategorizedTemplates = () => { const templates = tempMeta; const categorized = { - web: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.template_id)?.name || t.name) === 'web'), - mobile: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.template_id)?.name || t.name) === 'mobile'), - tablet: templates.filter(t => categorizeTemplate(getTemplateMetadata(t.template_id)?.name || t.name) === 'tablet'), + web: templates.filter((t) => { + const metadata = getTemplateMetadata(t.template_id); + const templateName = metadata?.name || t.name || "Unknown Template"; + return categorizeTemplate(templateName) === "web"; + }), + mobile: templates.filter((t) => { + const metadata = getTemplateMetadata(t.template_id); + const templateName = metadata?.name || t.name || "Unknown Template"; + return categorizeTemplate(templateName) === "mobile"; + }), + tablet: templates.filter((t) => { + const metadata = getTemplateMetadata(t.template_id); + const templateName = metadata?.name || t.name || "Unknown Template"; + return categorizeTemplate(templateName) === "tablet"; + }), }; return categorized; }; @@ -112,8 +136,8 @@ const FilesPage: React.FC = () => { // Get filtered templates based on current filter const getFilteredTemplates = () => { const categorized = getCategorizedTemplates(); - - if (templateFilter === 'all') { + + if (templateFilter === "all") { // Return in order: web, mobile, tablet return [...categorized.web, ...categorized.mobile, ...categorized.tablet]; } else { @@ -141,37 +165,40 @@ const FilesPage: React.FC = () => { if (filename === "Untitled") { return { isValid: false, - message: "cannot update Untitled file! Use Save As Button to save." + message: "cannot update Untitled file! Use Save As Button to save.", }; } else if (filename === "" || !filename) { return { isValid: false, - message: "Filename cannot be empty" + message: "Filename cannot be empty", }; } else if (filename.length > 30) { return { isValid: false, - message: "Filename too long" + message: "Filename too long", }; } else if (/^[a-zA-Z0-9- ]*$/.test(filename) === false) { return { isValid: false, - message: "Special Characters cannot be used" + message: "Special Characters cannot be used", }; } else if (await store._checkKey(filename)) { return { isValid: false, - message: "Filename already exists" + message: "Filename already exists", }; } return { isValid: true, - message: "" + message: "", }; }; // Create new file with template - const createNewFileWithTemplate = async (templateId: number, fileName: string) => { + const createNewFileWithTemplate = async ( + templateId: number, + fileName: string + ) => { try { // Validate filename first const validation = await _validateName(fileName); @@ -197,7 +224,9 @@ const FilesPage: React.FC = () => { } // Find the active footer index, default to 1 if none found - const activeFooter = templateData.footers?.find(footer => footer.isActive); + const activeFooter = templateData.footers?.find( + (footer) => footer.isActive + ); const activeFooterIndex = activeFooter ? activeFooter.index : 1; const now = new Date().toISOString(); @@ -212,10 +241,12 @@ const FilesPage: React.FC = () => { ); await store._saveFile(newFile); - - setToastMessage(`File "${fileName}" created with ${templateData.template}`); + + setToastMessage( + `File "${fileName}" created with ${templateData.template}` + ); setShowToast(true); - + // Reset modal state setShowFileNamePrompt(false); setSelectedTemplateForFile(null); @@ -223,10 +254,15 @@ const FilesPage: React.FC = () => { setShowTemplateModal(false); // Dismiss the template modal updateSelectedFile(fileName); - updateBillType(1); - history.replace(`/app/editor/${encodeURIComponent(fileName)}`); + updateBillType(activeFooterIndex); + + // Add 200ms timeout for routing + setTimeout(() => { + const link = document.createElement("a"); + link.href = `/app/editor/${fileName}`; + link.click(); + }, 200); } catch (error) { - console.error("Error creating file:", error); setToastMessage("Failed to create file"); setShowToast(true); } @@ -236,17 +272,14 @@ const FilesPage: React.FC = () => { const renderTemplateModal = () => { const filteredTemplates = getFilteredTemplates(); const categorized = getCategorizedTemplates(); - + return ( Choose Template - + @@ -254,133 +287,222 @@ const FilesPage: React.FC = () => { {/* Filter Segment */} -
- setTemplateFilter(e.detail.value as "all" | "web" | "mobile" | "tablet")} +
+ + setTemplateFilter( + e.detail.value as "all" | "web" | "mobile" | "tablet" + ) + } style={{ - background: isDarkMode ? "var(--ion-color-step-150)" : "var(--ion-background-color)", + background: isDarkMode + ? "var(--ion-color-step-150)" + : "var(--ion-background-color)", borderRadius: "8px", padding: "3px", - border: `1px solid ${isDarkMode ? "var(--ion-color-step-250)" : "var(--ion-color-step-150)"}`, + border: `1px solid ${ + isDarkMode + ? "var(--ion-color-step-250)" + : "var(--ion-color-step-150)" + }`, boxShadow: "none", - '--background': isDarkMode ? 'var(--ion-color-step-150)' : 'var(--ion-background-color)', - '--background-checked': isDarkMode ? 'var(--ion-color-primary)' : 'var(--ion-color-primary)', - '--color': isDarkMode ? '#ffffff' : '#000000', - '--color-checked': '#ffffff' + "--background": isDarkMode + ? "var(--ion-color-step-150)" + : "var(--ion-background-color)", + "--background-checked": isDarkMode + ? "var(--ion-color-primary)" + : "var(--ion-color-primary)", + "--color": isDarkMode ? "#ffffff" : "#000000", + "--color-checked": "#ffffff", }} > - - - - All ({categorized.web.length + categorized.mobile.length + categorized.tablet.length}) + + All ( + {categorized.web.length + + categorized.mobile.length + + categorized.tablet.length} + ) - - - + Web ({categorized.web.length}) - - - + Mobile ({categorized.mobile.length}) - - - + Tablet ({categorized.tablet.length}) @@ -389,31 +511,37 @@ const FilesPage: React.FC = () => {
{filteredTemplates.length === 0 ? ( -
- + -

+

No Templates Found

- No templates found for {templateFilter === "all" ? "this filter" : templateFilter} category + No templates found for{" "} + {templateFilter === "all" ? "this filter" : templateFilter}{" "} + category

) : ( @@ -423,22 +551,36 @@ const FilesPage: React.FC = () => { {/* Web Templates Section */} {categorized.web.length > 0 && ( <> -
- +
+ Web Templates ({categorized.web.length})
- {categorized.web.map((template) => renderTemplateItem(template))} - {(categorized.mobile.length > 0 || categorized.tablet.length > 0) && ( + {categorized.web.map((template) => + renderTemplateItem(template, "web") + )} + {(categorized.mobile.length > 0 || + categorized.tablet.length > 0) && (
)} @@ -447,21 +589,34 @@ const FilesPage: React.FC = () => { {/* Mobile Templates Section */} {categorized.mobile.length > 0 && ( <> -
- +
+ Mobile Templates ({categorized.mobile.length})
- {categorized.mobile.map((template) => renderTemplateItem(template))} + {categorized.mobile.map((template) => + renderTemplateItem(template, "mobile") + )} {categorized.tablet.length > 0 && (
)} @@ -471,29 +626,43 @@ const FilesPage: React.FC = () => { {/* Tablet Templates Section */} {categorized.tablet.length > 0 && ( <> -
- +
+ Tablet Templates ({categorized.tablet.length})
- {categorized.tablet.map((template) => renderTemplateItem(template))} + {categorized.tablet.map((template) => + renderTemplateItem(template, "tablet") + )} )} )} - {templateFilter !== "all" && ( - filteredTemplates.map((template) => renderTemplateItem(template)) - )} + {templateFilter !== "all" && + filteredTemplates.map((template) => + renderTemplateItem(template, "filtered") + )} )}
@@ -503,48 +672,87 @@ const FilesPage: React.FC = () => { }; // Helper function to render individual template items - const renderTemplateItem = (template: any) => { - const metadata = getTemplateMetadata(template.templateId); - const category = categorizeTemplate(metadata?.name || template.template); - + const renderTemplateItem = (template: any, keyPrefix?: string) => { + const metadata = getTemplateMetadata( + template.templateId || template.template_id + ); + const templateName = + metadata?.name || + template.template || + template.name || + "Unknown Template"; + const category = categorizeTemplate(templateName); + + // Get the template data from DATA to access footers + const templateData = DATA[template.templateId || template.template_id]; + const footers = templateData?.footers || []; + return (
handleTemplateSelect(template.templateId)} + key={ + keyPrefix + ? `${keyPrefix}-${template.templateId || template.template_id}` + : template.templateId || template.template_id + } + onClick={() => + handleTemplateSelect(template.templateId || template.template_id) + } style={{ - border: `1px solid ${isDarkMode ? "var(--ion-color-step-200)" : "var(--ion-color-step-150)"}`, + border: `1px solid ${ + isDarkMode + ? "var(--ion-color-step-200)" + : "var(--ion-color-step-150)" + }`, borderRadius: "8px", padding: "12px", marginBottom: "12px", cursor: "pointer", - backgroundColor: isDarkMode ? "var(--ion-color-step-50)" : "var(--ion-background-color)", + backgroundColor: isDarkMode + ? "var(--ion-color-step-50)" + : "var(--ion-background-color)", display: "flex", alignItems: "center", gap: "12px", - transition: "all 0.2s ease" + transition: "all 0.2s ease", }} onMouseOver={(e) => { - e.currentTarget.style.backgroundColor = isDarkMode ? "var(--ion-color-step-100)" : "var(--ion-color-step-50)"; - e.currentTarget.style.borderColor = isDarkMode ? "var(--ion-color-step-300)" : "var(--ion-color-step-200)"; + e.currentTarget.style.backgroundColor = isDarkMode + ? "var(--ion-color-step-100)" + : "var(--ion-color-step-50)"; + e.currentTarget.style.borderColor = isDarkMode + ? "var(--ion-color-step-300)" + : "var(--ion-color-step-200)"; }} onMouseOut={(e) => { - e.currentTarget.style.backgroundColor = isDarkMode ? "var(--ion-color-step-50)" : "var(--ion-background-color)"; - e.currentTarget.style.borderColor = isDarkMode ? "var(--ion-color-step-200)" : "var(--ion-color-step-150)"; + e.currentTarget.style.backgroundColor = isDarkMode + ? "var(--ion-color-step-50)" + : "var(--ion-background-color)"; + e.currentTarget.style.borderColor = isDarkMode + ? "var(--ion-color-step-200)" + : "var(--ion-color-step-150)"; }} > {/* Template Image */} -
+
{metadata?.ImageUri ? ( { style={{ width: "100%", height: "100%", - objectFit: "cover" + objectFit: "contain", }} /> ) : ( - )}
- + {/* Template Info */}
-

- {metadata?.name || template.template} +

+ {templateName}

-

- {template.footers.length} footer{template.footers.length !== 1 ? 's' : ''} +

+ {footers.length} footer{footers.length !== 1 ? "s" : ""}

{/* Category Badge */} -
+
{category}
- + {/* Arrow Icon */} -
@@ -626,27 +856,27 @@ const FilesPage: React.FC = () => { fontWeight: "400", }} > - Invoice App - {" "}Invoice App + objectFit: "contain", + }} + />{" "} + Invoice App - - history.push("/app/settings")} style={{ fontSize: "1.2em" }} > @@ -657,24 +887,36 @@ const FilesPage: React.FC = () => { {/* Template Creation Section */} -
-
-

+
+
+

Create New File

@@ -682,14 +924,16 @@ const FilesPage: React.FC = () => { {/* Desktop: Template Cards - Show only first 3 */} {!isSmallScreen && ( <> -
+
{getAvailableTemplates() .slice(0, 3) .map((template) => { @@ -697,7 +941,9 @@ const FilesPage: React.FC = () => { return (
handleTemplateSelect(template.templateId)} + onClick={() => + handleTemplateSelect(template.templateId) + } style={{ border: "2px solid var(--ion-color-light)", borderRadius: "12px", @@ -708,32 +954,38 @@ const FilesPage: React.FC = () => { display: "flex", alignItems: "center", gap: "16px", - boxShadow: "0 2px 8px rgba(0,0,0,0.1)" + boxShadow: "0 2px 8px rgba(0,0,0,0.1)", }} onMouseOver={(e) => { - e.currentTarget.style.borderColor = "var(--ion-color-primary)"; + e.currentTarget.style.borderColor = + "var(--ion-color-primary)"; e.currentTarget.style.transform = "translateY(-4px)"; - e.currentTarget.style.boxShadow = "0 8px 24px rgba(0,0,0,0.15)"; + e.currentTarget.style.boxShadow = + "0 8px 24px rgba(0,0,0,0.15)"; }} onMouseOut={(e) => { - e.currentTarget.style.borderColor = "var(--ion-color-light)"; + e.currentTarget.style.borderColor = + "var(--ion-color-light)"; e.currentTarget.style.transform = "translateY(0)"; - e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.1)"; + e.currentTarget.style.boxShadow = + "0 2px 8px rgba(0,0,0,0.1)"; }} > {/* Template Image */} -
+
{metadata?.ImageUri ? ( { style={{ width: "100%", height: "100%", - objectFit: "cover" + objectFit: "contain", }} /> ) : ( - )}
- + {/* Template Info */}
-

+

{metadata?.name || template.template}

-

+

{template.footers.length} footer(s)

- + {/* Arrow Icon */} -
@@ -798,63 +1057,76 @@ const FilesPage: React.FC = () => { alignItems: "center", justifyContent: "center", gap: "16px", - boxShadow: "0 2px 8px rgba(0,0,0,0.05)" + boxShadow: "0 2px 8px rgba(0,0,0,0.05)", }} onMouseOver={(e) => { - e.currentTarget.style.borderColor = "var(--ion-color-primary)"; + e.currentTarget.style.borderColor = + "var(--ion-color-primary)"; e.currentTarget.style.transform = "translateY(-4px)"; - e.currentTarget.style.boxShadow = "0 8px 24px rgba(0,0,0,0.1)"; + e.currentTarget.style.boxShadow = + "0 8px 24px rgba(0,0,0,0.1)"; }} onMouseOut={(e) => { - e.currentTarget.style.borderColor = "var(--ion-color-light)"; + e.currentTarget.style.borderColor = + "var(--ion-color-light)"; e.currentTarget.style.transform = "translateY(0)"; - e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.05)"; + e.currentTarget.style.boxShadow = + "0 2px 8px rgba(0,0,0,0.05)"; }} > {/* Plus Icon */} -
- +
- + {/* More Info */}
-

+

More Templates

-

+

View all available templates

- + {/* Arrow Icon */} -
@@ -864,14 +1136,14 @@ const FilesPage: React.FC = () => { {/* Mobile: Show template previews */} {isSmallScreen && ( -
{getAvailableTemplates() @@ -885,32 +1157,46 @@ const FilesPage: React.FC = () => { style={{ minWidth: "110px", width: "110px", - border: `1px solid ${isDarkMode ? "var(--ion-color-step-200)" : "var(--ion-color-step-150)"}`, + border: `1px solid ${ + isDarkMode + ? "var(--ion-color-step-200)" + : "var(--ion-color-step-150)" + }`, borderRadius: "8px", padding: "12px", cursor: "pointer", - backgroundColor: isDarkMode ? "var(--ion-color-step-50)" : "var(--ion-background-color)", + backgroundColor: isDarkMode + ? "var(--ion-color-step-50)" + : "var(--ion-background-color)", display: "flex", flexDirection: "column", alignItems: "center", gap: "8px", transition: "all 0.2s ease", boxShadow: "0 1px 3px rgba(0,0,0,0.1)", - flexShrink: 0 // Prevent cards from shrinking + flexShrink: 0, // Prevent cards from shrinking }} > {/* Template Image */} -
+
{metadata?.ImageUri ? ( { style={{ width: "100%", height: "100%", - objectFit: "cover" + objectFit: "cover", }} /> ) : ( - )}
- + {/* Template Name */}
-

+

{metadata?.name || template.template}

); })} - + {/* Plus icon card to show more templates */}
setShowTemplateModal(true)} style={{ minWidth: "110px", width: "110px", - border: `2px dashed ${isDarkMode ? "var(--ion-color-step-300)" : "var(--ion-color-step-200)"}`, + border: `2px dashed ${ + isDarkMode + ? "var(--ion-color-step-300)" + : "var(--ion-color-step-200)" + }`, borderRadius: "8px", padding: "12px", cursor: "pointer", @@ -968,35 +1264,45 @@ const FilesPage: React.FC = () => { justifyContent: "center", gap: "8px", transition: "all 0.2s ease", - flexShrink: 0 // Prevent card from shrinking + flexShrink: 0, // Prevent card from shrinking }} > -
- +
- +
-

+

More

@@ -1015,7 +1321,7 @@ const FilesPage: React.FC = () => { {/* Template Modal for small screens */} {renderTemplateModal()} - + setShowToast(false)} @@ -1026,54 +1332,44 @@ const FilesPage: React.FC = () => { /> {/* File Name Prompt Alert Wrapper */} - {showFileNamePrompt && selectedTemplateForFile !== null && getTemplateMetadata(selectedTemplateForFile) && ( - { - setShowFileNamePrompt(false); - setSelectedTemplateForFile(null); - setNewFileName(""); - }} - header="Create New File" - message={`Create a new ${getTemplateMetadata(selectedTemplateForFile)?.name} file`} - inputs={[ - { - name: "filename", - type: "text", - value: newFileName, - placeholder: "Enter file name", - }, - ]} - buttons={[ - { - text: "Cancel", - role: "cancel", - handler: () => { - setSelectedTemplateForFile(null); - console.log("File creation cancelled"); - setNewFileName(""); + {showFileNamePrompt && + selectedTemplateForFile !== null && + getTemplateMetadata(selectedTemplateForFile) && ( + { + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + setNewFileName(""); + }} + header="Create New File" + message={`Create a new ${ + getTemplateMetadata(selectedTemplateForFile)?.name + } file`} + inputs={[ + { + name: "filename", + type: "text", + value: newFileName, + placeholder: "Enter file name", }, - }, - { - text: "Create", - handler: async (data) => { - const fileName = data.filename?.trim(); - if (!fileName) { - setToastMessage("Please enter a file name"); - setShowToast(true); - // Clear the filename and close the alert when validation fails - setNewFileName(""); - setShowFileNamePrompt(false); + ]} + buttons={[ + { + text: "Cancel", + role: "cancel", + handler: () => { setSelectedTemplateForFile(null); - return false; // Prevent alert from closing automatically - } - - if (selectedTemplateForFile) { - // Validate the filename before creating - const validation = await _validateName(fileName); - if (!validation.isValid) { - setToastMessage(validation.message); + setNewFileName(""); + }, + }, + { + text: "Create", + handler: async (data) => { + const fileName = data.filename?.trim(); + if (!fileName) { + setToastMessage("Please enter a file name"); setShowToast(true); // Clear the filename and close the alert when validation fails setNewFileName(""); @@ -1081,17 +1377,33 @@ const FilesPage: React.FC = () => { setSelectedTemplateForFile(null); return false; // Prevent alert from closing automatically } - - // If validation passes, create the file - await createNewFileWithTemplate(selectedTemplateForFile, fileName); - return true; // Allow alert to close - } - return false; + + if (selectedTemplateForFile) { + // Validate the filename before creating + const validation = await _validateName(fileName); + if (!validation.isValid) { + setToastMessage(validation.message); + setShowToast(true); + // Clear the filename and close the alert when validation fails + setNewFileName(""); + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + return false; // Prevent alert from closing automatically + } + + // If validation passes, create the file + await createNewFileWithTemplate( + selectedTemplateForFile, + fileName + ); + return true; // Allow alert to close + } + return false; + }, }, - }, - ]} - /> - )} + ]} + /> + )} ); }; diff --git a/src/pages/Home.css b/src/pages/Home.css index 826807c..a3d6b33 100644 --- a/src/pages/Home.css +++ b/src/pages/Home.css @@ -1,20 +1,8 @@ #container { - max-height: 100vh; - /* position: relative; */ - /* border: #eb24fd; */ - /* border: 10px solid #eb24fd; */ - /* border-width: 20px; */ - /* margin: 100px; */ - /* height: min-content; */ - /* overflow: auto; */ - /* position: relative; */ - /* width: 100%; */ - /* min-height: 400px; */ - /* Add smooth scrolling behavior */ - /* scroll-behavior: smooth; */ - /* Ensure proper scroll on mobile devices */ - /* -webkit-overflow-scrolling: touch; */ + height: min-content; + overflow: hidden; } + #container, #workbookControl, #tableeditor, @@ -23,9 +11,33 @@ max-height: 100vh; /* overflow: hidden; */ } -#tableeditor{ +#tableeditor { height: 100vh; } + +/* Remove bottom padding from Home page content */ +ion-page ion-content { + --padding-bottom: 0px !important; + padding-bottom: 0px !important; +} + +/* SocialCalc specific fixes */ +#te_griddiv { + margin-bottom: 0 !important; + padding-bottom: 0 !important; +} + +/* Force SocialCalc container to not have extra bottom space */ +.SocialCalc-spreadsheet { + margin-bottom: 0 !important; + padding-bottom: 0 !important; +} + +/* Ensure the spreadsheet control fills available space properly */ +#tableeditor > div { + margin-bottom: 0 !important; + padding-bottom: 0 !important; +} /* Dark mode fixes for Home page */ .dark-theme ion-page { --ion-background-color: #0d1117 !important; @@ -712,3 +724,17 @@ ion-content { ion-fab[vertical="bottom"][horizontal="end"] { position: fixed !important; } + +/* Spin animation for auto-save indicator */ +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.spin-animation { + animation: spin 1.5s linear infinite; +} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 3e12f9e..5e589a3 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -16,67 +16,66 @@ import { IonGrid, IonRow, IonCol, - IonCard, - IonCardContent, IonSegment, IonSegmentButton, IonFab, IonFabButton, + IonPopover, + IonList, + IonItem, + IonCheckbox, isPlatform, } from "@ionic/react"; -import { APP_NAME, DATA } from "../templates"; +import { DATA } from "../templates"; import * as AppGeneral from "../components/socialcalc/index.js"; import { useEffect, useState, useRef } from "react"; -import { Local, File } from "../components/Storage/LocalStorage"; +import { File } from "../components/Storage/LocalStorage"; import { - checkmark, checkmarkCircle, - pencil, - saveSharp, syncOutline, closeOutline, textOutline, ellipsisVertical, shareSharp, - cloudDownloadOutline, - wifiOutline, downloadOutline, createOutline, - refreshOutline, arrowBack, documentText, folder, + saveOutline, + toggleOutline, + saveSharp, } from "ionicons/icons"; import "./Home.css"; import FileOptions from "../components/FileMenu/FileOptions"; import Menu from "../components/Menu/Menu"; -import PWAInstallPrompt from "../components/PWAInstallPrompt"; -import { usePWA } from "../hooks/usePWA"; import { useTheme } from "../contexts/ThemeContext"; import { useInvoice } from "../contexts/InvoiceContext"; -import { useHistory, useParams } from "react-router-dom"; -import InvoiceForm from "../components/InvoiceForm"; +import { useHistory, useLocation, useParams } from "react-router-dom"; import DynamicInvoiceForm from "../components/DynamicInvoiceForm"; -// import WalletConnection from "../components/wallet/WalletConnection"; -import { - isDefaultFileEmpty, - generateUntitledFilename, - isQuotaExceededError, - getQuotaExceededMessage, -} from "../utils/helper"; -import { TemplateInitializer } from "../utils/templateInitializer"; -import { TemplateManager } from "../utils/templateManager"; -// import { cloudService } from "../services/cloud-service"; +import { isQuotaExceededError, getQuotaExceededMessage } from "../utils/helper"; +import { getAutoSaveEnabled } from "../utils/settings"; +import { backgroundClip } from "html2canvas/dist/types/css/property-descriptors/background-clip"; const Home: React.FC = () => { const { isDarkMode } = useTheme(); - const { selectedFile, billType, store, updateSelectedFile, updateBillType, activeTemplateData, updateActiveTemplateData } = - useInvoice(); + const { + selectedFile, + billType, + store, + updateSelectedFile, + updateBillType, + activeTemplateData, + updateActiveTemplateData, + } = useInvoice(); const history = useHistory(); const [fileNotFound, setFileNotFound] = useState(false); const [templateNotFound, setTemplateNotFound] = useState(false); + const { fileName } = useParams<{ fileName: string }>(); + + const location = useLocation(); const [showMenu, setShowMenu] = useState(false); const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); @@ -88,6 +87,12 @@ const Home: React.FC = () => { const [saveAsFileName, setSaveAsFileName] = useState(""); const [saveAsOperation, setSaveAsOperation] = useState<"local" | null>(null); + // Autosave state + const [isAutoSaveEnabled, setIsAutoSaveEnabled] = useState( + getAutoSaveEnabled() + ); + const [showSavePopover, setShowSavePopover] = useState(false); + // Color picker state const [showColorModal, setShowColorModal] = useState(false); const [colorMode, setColorMode] = useState<"background" | "font">( @@ -103,10 +108,6 @@ const Home: React.FC = () => { // Invoice form state const [showInvoiceForm, setShowInvoiceForm] = useState(false); - // Error state for initialization failures - // const [initError, setInitError] = useState(false); - // const [fileIsEmpty, setFileIsEmpty] = useState(false); - // Available colors for sheet themes const availableColors = [ { name: "red", label: "Red", color: "#ff4444" }, @@ -161,7 +162,6 @@ const Home: React.FC = () => { }, 100); } } catch (error) { - console.error("Error changing sheet color:", error); setToastMessage("Failed to change sheet color"); setToastColor("danger"); setShowToast(true); @@ -185,11 +185,6 @@ const Home: React.FC = () => { } }; - const openColorModal = (mode: "background" | "font") => { - setColorMode(mode); - setShowColorModal(true); - }; - const executeSaveAsWithFilename = async (filename: string) => { updateSelectedFile(filename); @@ -221,15 +216,17 @@ const Home: React.FC = () => { const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); const now = new Date().toISOString(); - + // Get template ID from active template data - const templateId = activeTemplateData ? activeTemplateData.templateId : billType; - + const templateId = activeTemplateData + ? activeTemplateData.templateId + : billType; + const file = new File( - now, - now, - content, - fileName, + now, + now, + content, + fileName, billType, templateId, false @@ -240,8 +237,6 @@ const Home: React.FC = () => { setToastColor("success"); setShowToast(true); } catch (error) { - console.error("Error saving file:", error); - // Check if the error is due to storage quota exceeded if (isQuotaExceededError(error)) { setToastMessage(getQuotaExceededMessage("saving files")); @@ -253,196 +248,354 @@ const Home: React.FC = () => { } }; - const activateFooter = (footer) => { + const handleSave = async () => { + console.log("💾 handleSave: Starting save", { fileName }); + + // If no file is selected, can't save + if (!fileName) { + console.log("⚠️ handleSave: No file selected, skipping save"); + return; + } + + try { + // Check if SocialCalc is ready + const socialCalc = (window as any).SocialCalc; + if (!socialCalc || !socialCalc.GetCurrentWorkBookControl) { + console.log("⚠️ handleSave: SocialCalc not ready, skipping save"); + return; + } + + const control = socialCalc.GetCurrentWorkBookControl(); + console.log("📋 handleSave: Control status", { + hasControl: !!control, + hasWorkbook: !!(control && control.workbook), + hasSpreadsheet: !!( + control && + control.workbook && + control.workbook.spreadsheet + ), + }); + + if (!control || !control.workbook || !control.workbook.spreadsheet) { + console.log("⚠️ handleSave: Control not ready, skipping save"); + return; + } + + console.log("📄 handleSave: Getting spreadsheet content"); + const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); + console.log("📊 handleSave: Content retrieved", { + contentLength: content.length, + }); + + // Get existing metadata and update + console.log("📂 handleSave: Getting existing file metadata"); + const data = await store._getFile(fileName); + console.log("📋 handleSave: Existing file data", { + hasData: !!data, + created: (data as any)?.created, + templateId: (data as any)?.templateId, + }); + + if (activeTemplateData) { + console.log("💾 handleSave: Creating and saving file object"); + const file = new File( + (data as any)?.created || new Date().toISOString(), + new Date().toISOString(), + content, + fileName, + billType, + activeTemplateData.templateId, + false + ); + await store._saveFile(file); + console.log("✅ handleSave: Save completed successfully"); + } else { + console.log("⚠️ handleSave: No active template data, skipping save"); + } + } catch (error) { + console.error("❌ handleSave: Error during save", error); + + // Check if the error is due to storage quota exceeded + if (isQuotaExceededError(error)) { + setToastMessage(getQuotaExceededMessage("auto-saving")); + setToastColor("danger"); + setShowToast(true); + } else { + // For auto-save errors, show a less intrusive message + setToastMessage("Auto-save failed. Please save manually."); + setToastColor("warning"); + setShowToast(true); + } + } + }; + + const handleSaveClick = async () => { + console.log("💾 handleSaveClick: Starting manual save"); + + if (!fileName) { + setToastMessage("No file selected to save."); + setToastColor("warning"); + setShowToast(true); + return; + } + + try { + // Check if SocialCalc is ready + const socialCalc = (window as any).SocialCalc; + if (!socialCalc || !socialCalc.GetCurrentWorkBookControl) { + setToastMessage("Spreadsheet not ready. Please wait and try again."); + setToastColor("warning"); + setShowToast(true); + return; + } + + const control = socialCalc.GetCurrentWorkBookControl(); + if (!control || !control.workbook || !control.workbook.spreadsheet) { + setToastMessage("Spreadsheet not ready. Please wait and try again."); + setToastColor("warning"); + setShowToast(true); + return; + } + + if (!activeTemplateData) { + setToastMessage("No template data available for saving."); + setToastColor("warning"); + setShowToast(true); + return; + } + + // Call the main save function + await handleSave(); + + // Show success toast for manual save + setToastMessage("File saved successfully!"); + setToastColor("success"); + setShowToast(true); + console.log("✅ handleSaveClick: Manual save completed successfully"); + } catch (error) { + console.error("❌ handleSaveClick: Error during manual save", error); + + if (isQuotaExceededError(error)) { + setToastMessage(getQuotaExceededMessage("saving files")); + } else { + setToastMessage("Failed to save file. Please try again."); + } + setToastColor("danger"); + setShowToast(true); + } + }; + + const activateFooter = (footer) => { + console.log("🦶 activateFooter: Starting footer activation", { footer }); // Only activate footer if SocialCalc is properly initialized try { const tableeditor = document.getElementById("tableeditor"); const socialCalc = (window as any).SocialCalc; - + console.log("🔍 activateFooter: Checking DOM and SocialCalc", { + hasTableEditor: !!tableeditor, + hasSocialCalc: !!socialCalc, + hasGetCurrentWorkBookControl: !!( + socialCalc && socialCalc.GetCurrentWorkBookControl + ), + }); + // Check if SocialCalc and WorkBook control are properly initialized if (tableeditor && socialCalc && socialCalc.GetCurrentWorkBookControl) { const control = socialCalc.GetCurrentWorkBookControl(); + console.log("📋 activateFooter: Control status", { + hasControl: !!control, + hasWorkbook: !!(control && control.workbook), + hasSpreadsheet: !!( + control && + control.workbook && + control.workbook.spreadsheet + ), + }); if (control && control.workbook && control.workbook.spreadsheet) { + console.log( + "✅ activateFooter: All requirements met, activating footer" + ); AppGeneral.activateFooterButton(footer); } else { - console.log("SocialCalc WorkBook not ready for footer activation, skipping..."); + console.log( + "⚠️ activateFooter: SocialCalc WorkBook not ready for footer activation, skipping" + ); } } else { - console.log("SocialCalc not ready for footer activation, skipping..."); + console.log( + "⚠️ activateFooter: SocialCalc not ready for footer activation, skipping" + ); } } catch (error) { - console.log("Error activating footer, SocialCalc might not be ready:", error); + console.error("❌ activateFooter: Error activating footer", error); } - }; - -const initializeApp = async () => { - - try { - // Initialize template system first - const isTemplateInitialized = await TemplateInitializer.isInitialized(); - if (!isTemplateInitialized) { - await TemplateInitializer.initializeApp(); - } - - // Prioritize URL parameter over context to ensure fresh state - let fileToLoad=selectedFile; + }; - // If no file is specified, redirect to files page - if (!fileToLoad || fileToLoad === "") { - // console.log("No file specified, redirecting to files"); - history.push("/app/files"); - return; - } + const initializeApp = async () => { + console.log("🚀 initializeApp: Starting initialization", { fileName }); - // Check if the file exists in storage - console.log("file to load", fileToLoad); - const fileExists = await store._checkKey(fileToLoad); - if (!fileExists) { - console.log(`File "${fileToLoad}" not found`); - setFileNotFound(true); - return; - } + try { + // Prioritize URL parameter over context to ensure fresh state + let fileToLoad = fileName; + console.log("📁 initializeApp: File to load", { fileToLoad }); - // Load the file - const fileData = await store._getFile(fileToLoad); - const decodedContent = decodeURIComponent(fileData.content); - - // Get template ID from file data - const templateId = fileData.templateId; - - // Check if template exists in the templates library - if (!DATA[templateId]) { - console.error(`Template ${templateId} not found in templates library`); - setTemplateNotFound(true); - setFileNotFound(false); - return; - } + // If no file is specified, redirect to files page + if (!fileToLoad || fileToLoad === "") { + console.log( + "⚠️ initializeApp: No file specified, redirecting to files" + ); + history.push("/app/files"); + return; + } - // Load template data - const templateData = DATA[templateId]; - updateActiveTemplateData(templateData); - console.log(templateData); - console.log("Template data loaded successfully", fileData); - // Initialize SocialCalc with the file content - // console.log(`Initializing SocialCalc for file: ${fileToLoad}`); - - // Wait a bit to ensure DOM elements are ready - setTimeout(() => { - try { - const currentControl = AppGeneral.getWorkbookInfo(); - console.log("Current workbook info:", currentControl); + // Check if the file exists in storage + console.log("🔍 initializeApp: Checking if file exists in storage"); + const fileExists = await store._checkKey(fileToLoad); + console.log("📋 initializeApp: File exists result", { fileExists }); - if (currentControl && currentControl.workbook) { - // SocialCalc is initialized, use viewFile - AppGeneral.viewFile(fileToLoad, decodedContent); - console.log("File loaded successfully with viewFile"); - } else { - // SocialCalc not initialized, initialize it first - console.log("SocialCalc not initialized, initializing..."); - AppGeneral.initializeApp(decodedContent); - console.log("File loaded successfully with initializeApp"); - } - } catch (error) { - console.error("Error checking SocialCalc state:", error); - // Fallback: try to initialize the app - try { - AppGeneral.initializeApp(decodedContent); - console.log("File loaded successfully with initializeApp (fallback)"); - } catch (initError) { - console.error("initializeApp failed:", initError); - throw new Error( - "Failed to load file: SocialCalc initialization error" - ); - } - } - - // Activate footer after initialization - setTimeout(() => { - activateFooter(fileData.billType); - }, 500); - }, 100); - console.log("success"); - // console.log("Successfully loaded file:", fileToLoad); - setFileNotFound(false); - setTemplateNotFound(false); - } catch (error) { - console.error("Error initializing app:", error); - // On error, show file not found + if (!fileExists) { + console.log("❌ initializeApp: File not found in storage"); setFileNotFound(true); - setTemplateNotFound(false); + return; } -}; - - useEffect(() => { - initializeApp(); - }, [selectedFile]); // Only depend on selectedFile to prevent loops with selectedFile updates + // Load the file + console.log("📖 initializeApp: Loading file data"); + const fileData = await store._getFile(fileToLoad); + const decodedContent = decodeURIComponent(fileData.content); + console.log("📄 initializeApp: File data loaded", { + fileDataKeys: Object.keys(fileData), + contentLength: decodedContent.length, + templateId: fileData.templateId, + billType: fileData.billType, + }); - const [autoSaveTimer, setAutoSaveTimer] = useState( - null - ); + // Get template ID from file data + const templateId = fileData.templateId; + console.log("🎨 initializeApp: Template ID from file", { templateId }); - const handleAutoSave = async () => { - try { - console.log("Auto-saving file..."); - - // If no file is selected, can't autosave - if (!selectedFile) { + // Check if template exists in the templates library + console.log("🔍 initializeApp: Checking if template exists in DATA"); + if (!DATA[templateId]) { + console.log("❌ initializeApp: Template not found in DATA library", { + templateId, + availableTemplates: Object.keys(DATA), + }); + setTemplateNotFound(true); + setFileNotFound(false); return; } - // Check if SocialCalc is ready - const socialCalc = (window as any).SocialCalc; - if (!socialCalc || !socialCalc.GetCurrentWorkBookControl) { - console.log("SocialCalc not ready for auto-save, skipping..."); - return; - } + // Load template data + console.log("✅ initializeApp: Template found, loading template data"); + const templateData = DATA[templateId]; + console.log("📊 initializeApp: Template data", { + templateId: templateData.templateId, + footersCount: templateData.footers?.length, + }); + updateActiveTemplateData(templateData); + // Initialize SocialCalc with the file content + console.log("⚙️ initializeApp: Starting SocialCalc initialization"); - const control = socialCalc.GetCurrentWorkBookControl(); - if (!control || !control.workbook || !control.workbook.spreadsheet) { - console.log("SocialCalc WorkBook not ready for auto-save, skipping..."); - return; - } + // Wait a bit to ensure DOM elements are ready + setTimeout(() => { + console.log("⏰ initializeApp: Timeout callback executing"); + try { + const currentControl = AppGeneral.getWorkbookInfo(); + console.log("📋 initializeApp: Current control status", { + hasControl: !!currentControl, + hasWorkbook: !!(currentControl && currentControl.workbook), + }); - const content = encodeURIComponent(AppGeneral.getSpreadsheetContent()); + if (currentControl && currentControl.workbook) { + // SocialCalc is initialized, use viewFile + console.log( + "✅ initializeApp: SocialCalc already initialized, using viewFile" + ); + AppGeneral.viewFile(fileToLoad, decodedContent); + } else { + // SocialCalc not initialized, initialize it first + console.log( + "🔧 initializeApp: SocialCalc not initialized, initializing app" + ); + AppGeneral.initializeApp(decodedContent); + } + } catch (error) { + console.error( + "❌ initializeApp: Error in SocialCalc initialization", + error + ); + // Fallback: try to initialize the app + try { + console.log("🔄 initializeApp: Attempting fallback initialization"); + AppGeneral.initializeApp(decodedContent); + } catch (initError) { + console.error( + "💥 initializeApp: Fallback initialization failed", + initError + ); + throw new Error( + "Failed to load file: SocialCalc initialization error" + ); + } + } - // Get existing metadata and update - const data = await store._getFile(selectedFile); - const file = new File( - (data as any)?.created || new Date().toISOString(), - new Date().toISOString(), - content, - selectedFile, - billType, - activeTemplateData ? activeTemplateData.templateId : billType, - false - ); - await store._saveFile(file); - updateSelectedFile(selectedFile); + // Activate footer after initialization + setTimeout(() => { + console.log("🦶 initializeApp: Activating footer", { + billType: fileData.billType, + }); + activateFooter(fileData.billType); + }, 500); + }, 100); + console.log("✅ initializeApp: Successfully completed initialization"); + setFileNotFound(false); + setTemplateNotFound(false); } catch (error) { - console.error("Error auto-saving file:", error); - - // Check if the error is due to storage quota exceeded - if (isQuotaExceededError(error)) { - setToastMessage(getQuotaExceededMessage("auto-saving")); - setToastColor("danger"); - setShowToast(true); - } else { - // For other errors during auto-save, show a less intrusive message - setToastMessage("Auto-save failed. Please save manually."); - setToastColor("warning"); - setShowToast(true); - } + console.error( + "💥 initializeApp: Caught error during initialization", + error + ); + // On error, show file not found + setFileNotFound(true); + setTemplateNotFound(false); } }; - + + useEffect(() => { + initializeApp(); + }, [fileName]); // Only depend on fileName to prevent loops with fileName updates + + useEffect(() => { + if (fileName) { + updateSelectedFile(fileName); + } + }, [fileName]); + + // Reset autosave to global setting when a new file is opened + useEffect(() => { + if (fileName) { + setIsAutoSaveEnabled(getAutoSaveEnabled()); + } + }, [fileName]); + + const [autoSaveTimer, setAutoSaveTimer] = useState( + null + ); + useEffect(() => { const debouncedAutoSave = () => { + // Only auto-save if enabled + if (!isAutoSaveEnabled) { + console.log("⚠️ debouncedAutoSave: Auto-save is disabled, skipping"); + return; + } + if (autoSaveTimer) { clearTimeout(autoSaveTimer); } const newTimer = setTimeout(() => { - handleAutoSave(); + handleSave(); setAutoSaveTimer(null); }, 1000); @@ -450,7 +603,7 @@ const initializeApp = async () => { }; let removeListener = () => {}; - + // Wait for SocialCalc to be ready before setting up the listener const setupListener = () => { try { @@ -470,7 +623,6 @@ const initializeApp = async () => { setTimeout(setupListener, 2000); } } catch (error) { - console.log("Error setting up cell change listener:", error); // Retry after a delay setTimeout(setupListener, 2000); } @@ -485,17 +637,26 @@ const initializeApp = async () => { clearTimeout(autoSaveTimer); } }; - }, [selectedFile, billType, autoSaveTimer]); + }, [fileName, billType, autoSaveTimer, isAutoSaveEnabled]); useEffect(() => { // Add a delay to ensure SocialCalc is initialized before activating footer + const timer = setTimeout(() => { activateFooter(billType); }, 1000); - + return () => clearTimeout(timer); }, [billType]); + useEffect(() => { + // Find the active footer index, default to 1 if none found + const activeFooter = activeTemplateData?.footers?.find( + (footer) => footer.isActive + ); + const activeFooterIndex = activeFooter ? activeFooter.index : 1; + updateBillType(activeFooterIndex); + }, [activeTemplateData]); // Effect to handle font color in dark mode useEffect(() => { if (isDarkMode && activeFontColor !== "#000000") { @@ -559,10 +720,8 @@ const initializeApp = async () => { }); useEffect(() => { - // Add a delay to ensure SocialCalc is initialized before activating footer - console.log("Selected file changed:", selectedFile); - console.log("activeTemplateData", activeTemplateData); - }, [selectedFile, activeTemplateData]); + updateSelectedFile(fileName); + }, [fileName]); return ( { - history.push("/app/files")} style={{ color: "white" }} > - +
{selectedFile} {selectedFile && ( - - setShowSavePopover(true)} style={{ - animation: autoSaveTimer - ? "spin 1s linear infinite" - : "none", + minWidth: "auto", + height: "32px", + position: "relative", }} - /> - + title="Save options" + > + + + + {/* Auto-save indicators (positioned absolutely when enabled) */} + {isAutoSaveEnabled && ( +
+ +
+ )} +
)}
@@ -642,13 +836,13 @@ const initializeApp = async () => { /> - {activeTemplateData && activeTemplateData.footers.length > 1 && ( - -
+
{ > {footersList}
- - )} - - - + + + + {fileNotFound ? ( -
- + -

+

File Not Found

-

- {selectedFile ? `The file "${selectedFile}" doesn't exist in your storage.` : "The requested file couldn't be found."} +

+ {selectedFile + ? `The file "${selectedFile}" doesn't exist in your storage.` + : "The requested file couldn't be found."}

- history.push("/app/files")} style={{ minWidth: "200px" }} @@ -711,43 +908,57 @@ const initializeApp = async () => {
) : templateNotFound ? ( -
- + -

+

Template Not Found

-

- The file information is not downloaded. Please download the file template to open this file. +

+ The file information is not downloaded. Please download the file + template to open this file.

-
- + history.push("/app/files")} style={{ minWidth: "140px" }} @@ -755,12 +966,14 @@ const initializeApp = async () => { Go to Files - { // Add download template functionality here - setToastMessage("Template download functionality coming soon"); + setToastMessage( + "Template download functionality coming soon" + ); setToastColor("warning"); setShowToast(true); }} @@ -772,11 +985,11 @@ const initializeApp = async () => {
) : ( -
-
-
-
-
+
+
+
+
+
)} {/* Toast for save notifications */} @@ -847,6 +1060,8 @@ const initializeApp = async () => { setShowActionsPopover={setShowActionsPopover} showColorModal={showColorModal} setShowColorPicker={setShowColorModal} + onSave={handleSave} + isAutoSaveEnabled={isAutoSaveEnabled} /> {/* Color Picker Modal */} @@ -1003,6 +1218,36 @@ const initializeApp = async () => { + {/* Save Options Popover */} + setShowSavePopover(false)} + > + + + + Save Now + + + + +

Enable Auto-save

+

Temporary setting for this file

+
+ { + setIsAutoSaveEnabled(e.detail.checked); + console.log("🔄 Auto-save toggled:", e.detail.checked); + }} + /> +
+
+
+
+ setShowMenu(false)} /> diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index eca850b..606c5d6 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -15,33 +15,20 @@ import { IonItem, IonLabel, IonList, - IonToggle, - IonInput, IonModal, IonToast, IonGrid, IonRow, IonCol, - IonSelect, - IonSelectOption, IonRange, IonPopover, + IonToggle, } from "@ionic/react"; import { saveOutline, - cloudUpload, - save, - print, - mail, settings, informationCircle, - moon, - sunny, - card, - alertCircle, createOutline, - trashOutline, - colorPaletteOutline, add, checkmark, pencil, @@ -49,34 +36,19 @@ import { cloudUploadOutline, imageOutline, downloadOutline, - notifications, wifiOutline, cloudOfflineOutline, - cloudDoneOutline, - refreshOutline, - bug, arrowBack, + flash, } from "ionicons/icons"; import SignatureCanvas from "react-signature-canvas"; import Menu from "../components/Menu/Menu"; -import { Local } from "../components/Storage/LocalStorage"; import { useTheme } from "../contexts/ThemeContext"; -import { useInvoice } from "../contexts/InvoiceContext"; import { useHistory } from "react-router-dom"; -import PWAInstallPrompt from "../components/PWAInstallPrompt"; -import PWADemo from "../components/PWADemo"; -// import { usePushNotifications } from "../utils/pushNotifications"; import { usePWA } from "../hooks/usePWA"; import { resetUserOnboarding } from "../utils/helper"; +import { getAutoSaveEnabled, setAutoSaveEnabled } from "../utils/settings"; import "./SettingsPage.css"; -// import { -// cloudService, -// ServerFile, -// LoginCredentials, -// RegisterCredentials, -// } from "../services/cloud-service"; -// import { useAccount, useConnect, useDisconnect } from "@starknet-react/core"; -// import { useGetUserFileLimits } from "../hooks/useContractRead"; const SettingsPage: React.FC = () => { const [showMenu, setShowMenu] = useState(false); @@ -86,13 +58,8 @@ const SettingsPage: React.FC = () => { const [toastMessage, setToastMessage] = useState(""); const [isLoading, setIsLoading] = useState(false); const [showResetToast, setShowResetToast] = useState(false); + const [globalAutoSaveEnabled, setGlobalAutoSaveEnabled] = useState(getAutoSaveEnabled()); - // PWA features - // Push notifications disabled in local-only mode - // const { requestPermission, subscribe, getPermissionState } = - // usePushNotifications(); - const [notificationPermission, setNotificationPermission] = - useState("default"); const { isInstallable, isInstalled, isOnline, installApp } = usePWA(); // Signature state @@ -147,7 +114,7 @@ const SettingsPage: React.FC = () => { const signatures = JSON.parse(saved); setSavedSignatures(signatures); } catch (error) { - console.error("Error parsing saved signatures:", error); + // Error parsing saved signatures, use empty array setSavedSignatures([]); } } @@ -167,7 +134,7 @@ const SettingsPage: React.FC = () => { const logos = JSON.parse(saved); setSavedLogos(logos); } catch (error) { - console.error("Error parsing saved logos:", error); + // Error parsing saved logos, use empty array setSavedLogos([]); } } @@ -391,7 +358,6 @@ const SettingsPage: React.FC = () => { setShowToast(true); } } catch (error) { - console.error("Error saving signature:", error); setToastMessage("Error saving signature. Please try again."); setShowToast(true); } @@ -617,7 +583,6 @@ const SettingsPage: React.FC = () => { setToastMessage("Signature uploaded successfully!"); setShowToast(true); } catch (error) { - console.error("Error saving uploaded signature:", error); setToastMessage("Error saving signature. Please try again."); setShowToast(true); } @@ -651,7 +616,6 @@ const SettingsPage: React.FC = () => { setToastMessage("Logo saved successfully"); setShowToast(true); } catch (error) { - console.error("Error saving logo:", error); setToastMessage("Error saving logo. Please try again."); setShowToast(true); } @@ -811,7 +775,6 @@ const SettingsPage: React.FC = () => { setSelectedLogoFile(null); setLogoUploadPreview(null); } catch (error) { - console.error("Error saving uploaded logo:", error); setToastMessage("Error saving logo. Please try again."); setShowToast(true); } @@ -849,6 +812,13 @@ const SettingsPage: React.FC = () => { setShowResetToast(true); }; + const handleAutoSaveToggle = (enabled: boolean) => { + setGlobalAutoSaveEnabled(enabled); + setAutoSaveEnabled(enabled); + setToastMessage(`Auto-save ${enabled ? 'enabled' : 'disabled'} by default for new files`); + setShowToast(true); + }; + React.useEffect(() => { // Push notifications disabled in local-only mode // getPermissionState().then((state) => { @@ -914,6 +884,18 @@ const SettingsPage: React.FC = () => { + + + +

Auto-save by Default

+

Enable auto-save for newly opened files

+
+ handleAutoSaveToggle(e.detail.checked)} + /> +
diff --git a/src/services/exportAllAsPdf.ts b/src/services/exportAllAsPdf.ts index 21c6115..ebd212c 100644 --- a/src/services/exportAllAsPdf.ts +++ b/src/services/exportAllAsPdf.ts @@ -119,7 +119,6 @@ export const exportHTMLAsPDF = async ( onProgress?.("PDF generated successfully!"); } } catch (error) { - console.error("Error generating PDF:", error); - throw new Error("Failed to generate PDF. Please try again."); + throw new Error("Failed to generate PDF"); } }; diff --git a/src/services/exportAllSheetsAsPdf.ts b/src/services/exportAllSheetsAsPdf.ts index a718f3a..9b9cf70 100644 --- a/src/services/exportAllSheetsAsPdf.ts +++ b/src/services/exportAllSheetsAsPdf.ts @@ -18,20 +18,24 @@ export interface SheetData { } // Helper function to add header and footer to each page -const addHeaderAndFooter = (pdf: jsPDF, pageNumber: number, totalPages: number) => { +const addHeaderAndFooter = ( + pdf: jsPDF, + pageNumber: number, + totalPages: number +) => { const pageWidth = pdf.internal.pageSize.getWidth(); const pageHeight = pdf.internal.pageSize.getHeight(); - + // Get current date and time const now = new Date(); - const dateTimeString = now.toLocaleString('en-US', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: true + const dateTimeString = now.toLocaleString("en-US", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: true, }); // Set font for header/footer @@ -86,7 +90,7 @@ export const exportAllSheetsAsPDF = async ( // Remove the first empty page pdf.deletePage(1); - + // Track total pages for numbering let currentPageNumber = 0; const pagesInfo: Array<{ sheetIndex: number; pageInSheet: number }> = []; @@ -179,7 +183,7 @@ export const exportAllSheetsAsPDF = async ( "FAST" ); - heightLeft -= (pageHeight - 10); // Account for header/footer space + heightLeft -= pageHeight - 10; // Account for header/footer space // Add continuation pages for this sheet if needed while (heightLeft >= 0) { @@ -188,7 +192,7 @@ export const exportAllSheetsAsPDF = async ( currentPageNumber++; pageInSheet++; pagesInfo.push({ sheetIndex: i, pageInSheet: pageInSheet }); - + pdf.addImage( canvas.toDataURL("image/png", 0.95), "PNG", @@ -199,10 +203,9 @@ export const exportAllSheetsAsPDF = async ( undefined, "FAST" ); - heightLeft -= (pageHeight - 10); // Account for header/footer space + heightLeft -= pageHeight - 10; // Account for header/footer space } } - } finally { // Always remove the temporary container document.body.removeChild(tempContainer); @@ -212,7 +215,7 @@ export const exportAllSheetsAsPDF = async ( // Add headers and footers to all pages onProgress?.("Adding headers and footers..."); const totalPages = currentPageNumber; - + for (let pageNum = 1; pageNum <= totalPages; pageNum++) { pdf.setPage(pageNum); addHeaderAndFooter(pdf, pageNum, totalPages); @@ -228,7 +231,6 @@ export const exportAllSheetsAsPDF = async ( onProgress?.("PDF saved successfully!"); } } catch (error) { - console.error("Error generating combined PDF:", error); throw new Error("Failed to generate combined PDF. Please try again."); } }; diff --git a/src/services/exportAsCsv.ts b/src/services/exportAsCsv.ts index f5524f9..f4bc2e4 100644 --- a/src/services/exportAsCsv.ts +++ b/src/services/exportAsCsv.ts @@ -123,7 +123,6 @@ export function parseSocialCalcCSV(csvContent: string): string { return cleanedLines.join("\n"); } catch (error) { - console.error("Error parsing SocialCalc CSV:", error); - return csvContent; // Return original if parsing fails + throw new Error("Failed to export as CSV"); } } diff --git a/src/services/exportAsPdf.ts b/src/services/exportAsPdf.ts index 97d3574..426e532 100644 --- a/src/services/exportAsPdf.ts +++ b/src/services/exportAsPdf.ts @@ -11,20 +11,24 @@ export interface ExportOptions { } // Helper function to add header and footer to each page -const addHeaderAndFooter = (pdf: jsPDF, pageNumber: number, totalPages: number) => { +const addHeaderAndFooter = ( + pdf: jsPDF, + pageNumber: number, + totalPages: number +) => { const pageWidth = pdf.internal.pageSize.getWidth(); const pageHeight = pdf.internal.pageSize.getHeight(); - + // Get current date and time const now = new Date(); - const dateTimeString = now.toLocaleString('en-US', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: true + const dateTimeString = now.toLocaleString("en-US", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: true, }); // Set font for header/footer @@ -169,7 +173,6 @@ export const exportHTMLAsPDF = async ( onProgress?.("PDF generated successfully!"); } } catch (error) { - console.error("Error generating PDF:", error); throw new Error("Failed to generate PDF. Please try again."); } }; diff --git a/src/templates-meta.ts b/src/templates-meta.ts index dd3ff0c..afcdda5 100644 --- a/src/templates-meta.ts +++ b/src/templates-meta.ts @@ -1,45 +1,45 @@ export let tempMeta = [ { - name: "Mobile-Invoice-1", - template_id: 1001, + name: "Web-Invoice-1", + template_id: 3001, ImageUri: - "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + "iVBORw0KGgoAAAANSUhEUgAAAgQAAAIdCAYAAABC22XFAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AAAAtdEVYdENyZWF0aW9uIFRpbWUAU3VuIDMxIEF1ZyAyMDI1IDAxOjU4OjE1IEFNIElTVFMKXoEAACAASURBVHic7N15fBRF+vjxT3fPJJM7QBKOJdxHuA85giBGMegCAgYBAVEQV/2u6wXrwbJ4ISj7UzzWW/FAF0FRUVFADCgoIMohgYAcQYhAQghJIOSYo+v3xyRDJgdnkiHheb9eszDd1V3V4xb9dFV1leZ0OhVCCCGEuKRZdF33dRmEEEII4WMSDQghhBACi6Zpvi6DEEIIIXxMWgiEEEIIIQGBEEIIISQgEEIIIQQSEAghhBACCQiEEEIIAVgq+4Ty1oIQQghx/pTyzXyBlR4QgO8uRgghhKjJfPlQLV0GQgghhJCAQAghhBASEAhxSTl8+DDr168nJSUFp9N51seZpsmmTZtITU2twtIJIXxJAgIhLiJRUVF06dIFgPXr19O4cWMOHTrklWbChAlomkZ2djZXXHEFmqaxd+9erzQvv/wymqbx9NNPA1BYWEhCQgKNGjWiT58+tGzZkrZt2/L77797jvntt9/QNI177rnHs81ut/Pwww9Tv359LrvsMpo0aUJMTAw7duwAICgoCE3TynxeffXVKvl9hBBVp0oGFQohKsfBgwcZMWIEq1evxmq1ltk/duxYfvzxR+bPn8/06dM92xcsWICu69x6660A3HXXXXz++ef06NGDq6++mu3bt/P111/Tv39/tm7dSv369cvNf+DAgfzwww906NCBW265hcOHD7Nr1y5atWrlSdO0aVMmTZrkdVyPHj0q4/KFENVJVbIqOKUQl4zIyEjVuXNnpZRS69atU4AC1N133+1Jc+uttypAZWVlqaysLGWz2VS7du08+/fu3asANWTIEKWUUtu3b1eAGjlypDJN05Pu3//+twLUP/7xD6WUUlu2bPH6/uGHHypAxcfHq4KCgnLLGxgYqPr06VO5P4IQlzBf3kOly0CIi5zNZuOVV17hvffeK7MvPDycIUOGsGPHDjZv3gzAvHnzALjlllsAWLx4MQCTJk3yeqVp6tSpBAYG8sMPP5Sb78KFCwGYNWsW/v7+lXY9QoiLkwQEQlykim/e9913H82bN+fuu+9m06ZNZdKNHTsWgE8++QRwBwQREREMHz4cgD179gDQoEEDr+MCAwNp3rw5v//+e7lzh+zcuZPAwMAzNv+vW7fOa/zAn3/+eY5XKoS4GMgYAiEucmFhYSxcuJB+/foxYsQIz6DD4oBhyJAhREZG8vHHHzN48GD27dvHAw884BlzYJpmhec2TROLxVLuZCgulwuL5cz/RJQeQxAaGnpO1yeEuDhIQCBEDdCzZ09mzZrFP//5T/744w/g1IygVquVMWPG8NJLL/HQQw8Bp7oLwH3DBti/f78nmADIy8tj3759tG/fvtw8W7RoQUpKCocOHaJRo0YVlq1Ro0ZeAxqFEDWTdBkIcZEq3Yw/ZcoUrr/++nLT3nTTTQCsXbuWHj160LVrV8++QYMGAae6FIrNmzePgoICEhISyj3nsGHDAHjllVe8tq9fv/60rQ5CiJpJWgiEqEHmzZtH9+7d2bdvn9f2Pn36eOYVKH7VsFjv3r0ZMmQIH374IaGhoTRo0IDc3FzmzJlDixYtuP/++8vN629/+xuvvPIKs2bNIicnh4iICLZv387ixYuZO3eupxXi0KFDzJgxw+vYG264gY4dO1bilQshqpoEBELUIOHh4Z7xBKWNHz+ep556ivHjx5fZt2jRIh566CHeffddTpw4QUhICGPGjGHOnDkEBQWVm5e/vz9r167l/vvv5/333yc3N5fQ0FDGjx/PVVdd5Um3f/9+Hn30Ua9j27RpIwGBEDWMpsobXnwhJ9Q0We1QiCr222+/0aFDB69Bf9nZ2ezZs+e0bwU4HA62b99O+/bt8fPzO+v8CgsLSU5OplOnTmc10FAIcX58eQ+VgEAIIYS4SPjyHiqDCoUQQgghAYEQQgghqmhQYXmTnAghhBDi4lUlAYGMIRBCCCHOnS8fqKXLQAghhBASEAghhBBCAgIhhBBCIAGBEEIIIZCAQAghhBBIQCCEEEIIJCAQQgghBBIQCCGEEAIJCIQQQgiBBARCCCGEQAICIYQQQiABgRBCCCGQgEAIIYQQSEAghBBCCCQgEEIIIQRg8XUBhBDiUvXAlS5fF6HWeP4Hw9dFqPFqbQtBYmIiISEhAEybNo3nn3/es2/ChAk8/PDDTJs2jd69e3u2r1u3DqvVyuHDh0lNTWXQoEGEh4fTrVs35syZU+a8AAsWLKBbt25ERESQkJBAVlYW06ZNQ9M0z6dhw4bllvGhhx5i3rx5PProo7z11ltV8TNckBUrVjBy5EhfF0MIIUQ1qLUBQWmPPPII69at89o2fvx4NmzYwJ49ewD3zX3o0KE0bNiQ0aNH06BBA3bv3s306dOZOnUqCxcu9Dp+9erV3HbbbUyePJnk5GTGjh1LnTp1AOjfvz+rVq1i1apVLFq0qEx51q1bx9dff43NZuOrr76iXr16ZdLs3bsXp9NZWT/BOXnssccYPHiwz/IXQghRvS6ZgMAwDEaNGkV6erpnW0xMDH379uWTTz7B5XKxYMECbr31VjZu3Mi6det49tlniYyMJCEhgd69e7NmzRqvc7788stMnDiR8ePHExUVxY033lhu3n379i2z7ZlnniEjI4OXX36Z1NRUlixZ4rU/PT2dVq1aebVsVKfAwED+9re/+SRvIcTpKaUwTSdOVyFOV0E1fgpxuew4XXZcLjum6cBULpQyUShf/yziAl0SYwg0TSMhIYH09HRGjx5NdHQ0mqYBMG7cOObOnUunTp0wDIMhQ4bw6aefUqdOHerWres5R4cOHThy5IjXeXfv3k1sbGy5ef7+++/MnDkTgLi4uDL7x44dS7NmzRg4cCBffvklb7zxhtf++vXr88ILLzBs2LALufTz9vDDD/Pwww/7JG8hRPmUct90TeVEaXb00Fw067m14pV/2/be6ioAZYKrUEO5SqbQ0NFQgI4VA3+UCzTNgo4FTdfRNenLr6kuiYBAKYWmaXz88cfExsayYcMG7rnnHgBGjx7N5MmTeeaZZ7jtttvQdZ3IyEiysrLIyckhLCwMgP379xMTE+N13vr165OSklJunn369OHzzz8vd9+nn37K9OnTsVqtfPvttwAcOHCAJk2aeKW77777Lui6hRC1j6lcuFQh4X/JIPaGfOo1MlDK/XyuAabLrPBZ3d2yoNCKv6MwXSYul+mJCRTuYMDpANMFSoFp6jhcfpgOhTPXLNoGBXmQnmojKzUQe44NzbShND90TUfTLpkG6FrjkvgvVtwaUKdOHRYtWuT5DlC3bl2GDh3KTz/9xO233w5A7969iYqKYurUqWRmZvLGG2+wdOlShg4d6nXeoUOH8v7777No0SKOHj3K7Nmzyc7OPmN5RowYgWmabNiwAZfLxfr168sEA0op3nnnHVJTUy/08oUQtYRSJko5cZp5KP0kgcEOgsOcBIc5CQlzEhLuIrSuSWgd0709tOgT5iIk3P0Jq2sSHu6kTriDuuFOIuqZ1I+Ceg106jXQqRulUbe+RlRjjQZNNRo202jUzKRpqwKatS2gdbdC2nTJo1XnXNr2OkHs9Zn0SkinUacjYMvEqU7gMu2YpsvToiFqhkumhaBYp06dePvtt9myZYtn27hx4zh27BjNmjUDICAggNdee41x48bx2muvATB9+nTi4uJITEz0HDdp0iQSExM9I/EHDx7MqFGjAFi8eLEn8GjXrh3Jycme4zZv3kzr1q3Zt28fTZs29bRClHTw4EEmTZrErFmzmDp1aiX9EkKImk3hNO0UOrM5mZ+NSwV53mYq5nJA2v58jqUXYC9UoEDTITLanwZN/LFkZuC/ezd6QUHRGUEFB2N27ITdYuNwSh5ZR1yeFgPNcB9bv7E/mnJi+WMPtrTDKNMkw+4ATadBo6ZEDmpKSnQ+KZvzOZ4WjssRgMXwd3clSGtBjaCpSg7hNE2rNVFhbm4uycnJxMTEEBoaWmG61NRUHA4HLVq0qNT8N23aRExMDIGBgZV6XiHExeFc5yEwTSd2Vx75zjTC/nKEwZOCaNjU/9R+F+z8NZ8v/3uYwyn5KKXQNY0GLfwZem9D2jfMJvC9dzBWroTCQgBUWBjOkSM5MexGtm1TfPP6EY78UejugtA0Gja3Mfz+KJq3t+K3M5ng/76Evns3uNwtAGaLFpwcP4HDbbvw6/rj1AmPwmWPYO8GfxzZYegEousW9CoOCmrLPAS+vIdeEi0E5ys4OJhevXqdMV10dHSV5N+9e/cqOa8QovZRQHaW4oePMziw8yQuh/umEhxuoctV4bSO0bF9+i1GYiJaVpb7ID8/XF27Yh8yhKMn/Fm7OJ2DuwpwOd3HhkVZ6TsigqYxNmxHDxEw/38YSUngdLobEEJDscdfy/GO3di+zsHqLw7ToZeLoTeH0yjawYavHZw4XA/MQNCs6Lq0FFzM5L+OEELUUKe6CzQcBfDL0ix+33DCEwxY/DQ6XR1Kr+tCCNydjPWrr9CKxznpOq6YGApvSCA3sC7rvsxh9y8nPMGAf4BOtwEhdB1gw+I8ifFdIsaGDVA8N4mfH67L+1J41VWkZZls+DaT42kmf+xJJyfTTodYf/oMyyc8OgMXJzGVA6VMH/xK4mxJQCCEEDWUpmlouoYyYdevJ1n3eSYFJ1xF+6Bxm0D6DqlDvcLDWD74EA4cgKI3Esx69XAOH469bXuS151g83dZFOS6j9UtGq17BhM7pB4BATq2pK34L18GubnujHUds1078saMJdMSya/fOEnbAarQj5PZdjLTs/Hzt9Kqqz+9hzoIbpSDU+W6Bxsqma75YiUBgRBC1FC67m4dyDjkZM1nmaTvK6C4+zm8gZVrbo2gTSsIXPYN1p9+RHM43Dv9/XHGx2O/egB/HvJjxbxsMg4Ueo6tW99Kv4RwGrexomcdw+/jj9H37kUz3U/4ZmQU9htuwN6qLTt+PsnWlSew54Gmu3CZhTic+VgsFmwBVtp0tdEnwYGtfg4OMxfTdNaacWa1jQQEQghRY2kU5sP6r7LZszEXNIVuaASGWuh1XT26xAZg3bAWPTHR3dRvGCg/P1zdu+NMSOBYYSBrvzzC4d0n0XTQDQiua6HfjRG0uSwUa14uwZ99hmXrVvfEA4aBCg7Gce1A8vr044/dLtYuziE3y4mug+4IwuZnIyhMwzAMrFYrFqtG647QM95OQN0TONQJXKZDgoKLkAwqFEKIGiz7iIOTxwtp2yMQ3dBAg4hoG1ck1CPAKMRIS8PZvDmqaVOwWCEoENdf/4qzRUuykx1gGnToGwYodAMatvKn9+AQAoNBS8nEnn4EunQFXUczDGjQAMf1Q3EEh5LxZy6R0VbqRLlH+Ns5SWiTAMIjDQyLe1vxxHDtejkozC/gt+90HDn+aEoreiVRO83Vieokrx0KIYSPXOhrh8PuDCWqsa1opsKi/v+igYa6oaNME93pxImGWeLGq2lA0WyDLtNAKdAwARNNA8Nw/1vucrrcsxjiTq9wd1PouoapFE6HAtwzI5ouk5zsXNLTDtKkWWO6duuCw+HA4XC6/3Q6yM1x8NtqB5uWheDMrYNFs6HrlTNPgbx2eOGkhUAIIWowq9V9MzVNd+tASZquo/z8MJRCU0V39KJgQKHQDYVhmO4bkIZ7Z4l7kWHxftJXyh1MmKaJrmn4+evufAGXDlarFavVj4DAACx+fihU0bgEhVKKwBCTTv1MCu0n2fqtjiPfxFo0T4FMXuR7EhAIIUQNpGkaum6gg3tugAMH0OqEo5o2Q9u7B5WWjhYVhRkQAAUFaH/8gYqKwuzbF2dIcNEN/lSQoEzlfmOhgqdT95Or6V4LQdOLugJKPNGqU7PCWgwLVqsFitZOUEqhGy4spoXgULjsahcncwr4fY3C6QKLFoSOJt0HPiYBgRBC1FC6pmEtLEQ7cgQzJAR+34VutaJvTcKVmQl+fhi//AIhIWhBQZhBgTj93TMbFt/INV0rmt5YQ+Hu71coNMoGByWnSS4+Xtc1XC73jT89/TAZGWmEhgdjC7BRaLfjcrlQponFYsViWNEN8LNBjwEK3Sxg9wZwFupY9AAZU+BjEhAIIURNpcBl9UOvWxeVnQ2hoWjHT6CaNkXVq4fZoAF6QAC6xQIhITgiI1GWU//s67ruGfRX3JVQ8tzFN2dVNICg5M3aLHoFUdN0dN39dkN4nVD8/W3kZOWw/Pfl5BcUuI82FS7TxGqxYrFaqFunDvWj/kLM5XXQjGB2rbXgLASLIUGBL0lAIIQQNZiyWnBedllRs71CaaBrOqbLhakUhuEeA2CarnKXRS558y1uMdCUVtT/rzytBsX7i5WchljXdXTdIDQkDH8/BwX5/mi6jt1uB8DusHP8eDamy8ThcJCRcRR7oYOQ4KM07vQXCvOdHNhi4ixQWIxACQp8RAICIYSo4UqveAhgWCyeiYQAdN3w3OSLBxWWVtxiQNHYAMyibaZCae70FXUlGIaGYTUIshoEBNoIDg3GXuhEKTCVC5ejANM0cTicmKbC6XSBBn42OzF98rH6ZbJvo4kzz919YGhye6pu8osLIUQtVfwUb5YMDDTdfXNXeHURFAcIJQMLzdBOBQhFg/48XQzKu8VAwyD3RB5Bwf4EBAZgU1ZcLpNTScIwTROn3YXLBQ6Hu/XAz88PixUCr3KRn5fH/l8CMDQ/TKVX+QqJwpsEBEIIUVOVahk40/vrevHbAUXvJxa/VVDRIMJT2ZTTfK8BpdYqyj+ZT0CABUvRq4pWK5im8iqb6WdimiZHM46DgrA6QUV5GBgBeRQ4LBi6P7qylHmNUlQtCQiEEKKGcvfd655XCCua1KZkf3/J8QDFXQheT/7g1aVQZoxByeBAd7+uCO5pj+vUC8MW4O+VxjC8j9d1DaV0T9DgnuhIB6UwNDuK4rUOVNn8RJWSgEAIIWoq7dT4Ac+8AnDa4MBzqKahTOUZI6C5+wXK3IS9ugXKe8tALwowXApbgJ/nRl9RnsXlCgsPQ9N0rFZr0bLI3mVVmGjUjtkHawoJCIQQohYoHRic7sZezNNyYJza71Iuz/lKnqP08cXHFm/X9eLJkvRyn+pLH+9yugAX/v5W96uLBqW6CGQK/OomAYEQQtRCp143ND0349O1GngmGioayFc8rkDTNExlul9lVGa5xwG4TJP8vEI0DSxWS4m3GbzPX8zf5ufZWbZMEgz4ggQEQghRA2m4Zyoss73EQMPiJ3xN00pMJHSGoKA4ECjqQnCvW6B7BwtF4w+8j9XJz8vH6bSjaRYK8vOx+lnIPpaNqUzq1AkHDDTNPRfBsWNZhIWHER4eVtSqYKLrJg5nLqbFifQWVD8JCIQQoiYqNX4Ayt7sSzf3l552uHhfye8lX/UzTdPrGFO5vxe/pVAyr8J8cOTWw+VnkJNpIaiOhl6o43DVpfC4ibJb0A33fAUOZcWw2Ci0Wzl+wiAs1H0eP38TzcjHRSGKkMr/zcRpSUAghBA1kMWwYBgGuq57nv7P9k0DwNNqcKYWg+I/lVLouMcHmJieqYyVUphOnd2/OTmW6Y/LCdmZCrvDhisfNN2KcilwgsUP/G2KnCMaDVoX4HIadOirExbqKmrxcC+YJHxDAgIhhKiBNB3PPAReaxIUKd0qUHJ7cdqSx5VsMfDkUcHdWdd0zyuLuq6jGRBWD06edOJSOiHKwBak0BVouguLP2hOxfFjBigICjWp29iC4afRoKEOmgtdB8NC0YRHpvvNAwkOqpUEBEIIUcOVd+OuqCuhvGmOS6cp6XTHFR9jWDRatLfSrK2fZwwCuipaL8k9x4BSZtGyyYDC3cqAQtddKKVh6sXnVlAcEIhqJQGBEELUAqcbSHi6twy8piouZz6D0ucunfZUtwIYlqLBhsWDDlXRIoqergDT0yXgHjNYsjwaZZdcFNVJAgIhhKiRNIyicQGahmfNgPKe4ksHAmea4rjkecrrciidpuTfvaZCLl4z4TT5nmp50FBYAfsZyyaqhgQEQghRAxRNNFzBXq0oKCh/8iDPOcqZCrh060HJroHSLQYlt1Wk9GuPJiY6umcug9LTJZ8qq8JikW4CX5KAQAghajhd1zyLCFU0s2DJloLybuql1zQoPXbgdEFBRWMPSs5doFPUmoF2ahnmUtegy+KGPiUBgRBC1ADFPexe23QNw3C/CmgYGkqBy+XyBAXuhYQo85TvdY5SXQNeTf/lBBXlvb1wprEHFaUvZioTTcYO+JwEBEIIUQMUDc73ohVtyM0toKBAoVTRTfcsx+YVDwBE01CmWWrfmbkcdkzTVXRz14rPWCqTM59J18Fms3gmPBK+UWsbaBITEwkJcc90NW3aNJ5//nnPvgkTJvDwww8zbdo0evfu7dm+bt06rFYrhw8fJjU1lUGDBhEeHk63bt2YM2dOmfMCLFiwgG7duhEREUFCQgJZWVlMmzbN08ymaRoNGzb0KltBQYFnX+vWrZk+fTpOp9Oz3zRN2rZtS05ODjt37vQ6V/Hn9ttvP2M+QohaTtPQNJ2TeTrHc0M5cbKu+5Nb99TfT/PJPVmP3LwIck/W42R+pNcn72w+hVGkZVg5lK5zKF3jULrO4dKfI8YZP8eOmThdyrNyovCNS6aF4JFHHiE2NpY+ffp4to0fP55Zs2axZ88eWrVqxYIFCxg6dCgNGzbk8ssvJyYmht27d7NmzRrGjBnDX/7yFyIiIjzHr169mttuu4033niDa6+9ltWrV1OnTh0A+vfvzxNPPAGA1Wott0wbNmygoKCAqVOncvLkSU/QsWzZMnbt2sW7777LHXfcwapVqwAYPnw4U6dOpXfv3jRo0IAPPvig3HzS0tIIDAwkNDS0kn9FIYSvlNdlAGCaCmUqwAJa+f/WVAX32wR2TFPDNC/s2dJUEghcDGptC0FphmEwatQo0tPTPdtiYmLo27cvn3zyCS6XiwULFnDrrbeyceNG1q1bx7PPPktkZCQJCQn07t2bNWvWeJ3z5ZdfZuLEiYwfP56oqChuvPHGcvPu27dvuds1TeOKK65g0qRJvP766553ht977z1GjhzJ22+/TWBgIHFxccTFxWG1WunYsSNxcXHExMRUmM+1117LzTfffF6/kxDi4lRelwFQNIag+v8pd7dMllmz+PzPR8UzI4rqcUm0EGiaRkJCAunp6YwePZro6GjP//HGjRvH3Llz6dSpE4ZhMGTIED799FPq1KlD3bp1Pefo0KEDR44c8Trv7t27iY2NLTfP33//nZkzZwIQFxd32vJ16tSJ/Px8MjIyME2Tzz//nJSUFDp27Mjq1avp379/hceWl8+TTz5JWFjYafMUQtQsFbUQaBXtqGJKKfJO5mOarso5oabJOgY+dkkEBMWv0Xz88cfExsayYcMG7rnnHgBGjx7N5MmTeeaZZ7jtttvQdZ3IyEiysrLIycnx3Fj3799f5qm8fv36pKSklJtnnz59+Pzzz8+qfFu2bCEwMJCoqChmz55N06ZN2bt3L127duXtt98+bUBQXj7Dhg07q3yFEDVH6RYCDdAN93x/FgsEBGhYLBqmeWqiIl3H67tWPNjwDIMOFRWfo2QiQw/CNE9iupwVneqsWCzuIhlWHd1y+rKJqnNJdBkUtwbUqVOHRYsWeTVL1a1bl6FDh/LTTz9x++23A9C7d2+ioqKYOnUqmZmZvPHGGyxdupShQ4d6nXfo0KG8//77LFq0iKNHjzJ79myys7PPqWxr167l//2//8ddd90FwNy5c+nTpw9r1qzhsssuY8GCBWRmZp7TOZctW8bPP/98TscIIWqe4n/KbP6KsFCd0BCD8DCDsFD3n6W/h4UahJX8s4LP6c5R/AkLM6gTbiUsRCc0RLugT0CA+7VJ/0Adw0+aCXzlkmkhKNapUyfefvtttmzZ4tk2btw4jh07RrNmzQAICAjgtddeY9y4cbz22msATJ8+nbi4OBITEz3HTZo0icTEREaOHAnA4MGDGTVqFACLFy/2BB7t2rUjOTm5TLkuv/xy2rZty4QJE3jooYdITExk//79/Pzzz57uikWLFvH+++8zefLkcq+tvHwmT55M06ZNWbp06Xn9XkKIi89pewY0d3BQ3U3uSjnQNPPC8/W8/SjBgC9p6mwmtT6XE55hWsuaJDc3l+TkZGJiYk47Yj81NRWHw0GLFi2qsXQVS0lJwWaz0ahRI18XRQhxGg9ceS797wqX6cThyiffmUb9Ftnc/M9oGjbzJ+d4HrpeF8Pwr7KylimNUhQUFHAs8wiuC+wyQNkJr+PHro15fPl2LkZhNDZrBFYj8KyDhOd/MC6sDBcJX95DL4kWgvMVHBxMr169zpguOjq6Gkpz9i6WwEQIUXnK3CK0onEFvnwAU5X7loGMKvQtCQiEEKIG0q0GhkVH13XshYpCuwu0ShrxfzYUFBSaZOUoXM4LC0r8rYrwcE26DHxMAgIhhKiBdN29loFS4HDo5OVzai7jaqFwucDhANcFxiFWA3RNl4DAxy6JtwyEEKKmq3AeAl2vYE/VUgqUeRK4wPED4Lk4CQd8SwICIYSoIUqtPYiuu7sMNB+tG6zpNqByBvNpGrKWgY9Jl4EQQtQA5XUGaLp7fUBdV+jVPGOh0sB05QGVOVOhBAS+JAGBEELUABXe7zWw2UwMw91iUF2UgvyCUBz2PFwuxwWdy8+q4aNGDlGCBARCCFEDVLS4EbgXONJ1HcOozoBAoZRGgE3D5bqwJ3vDkEmJLgYSEAghRA1wuh4BX91MDaOwUl51LJ6l0DAMCQx8SBpphBCihrjY5oBVpj+oC7+NKNMdFFisFs+CTaL6SQuBEELUAKrE/5bZZ5q4XPZT9+ai1QyrMoBQSmG3OzCVyYUOLFTKXVgN7UwLMYoqJAGBEELUAFqJ/y3JMAwCAv2wF+ajyPPeqU5NbaxwBw7F0wMr07zg8thsCqtVRyk/z3bTVEVLJitPac0zTK+saRZsAX5oU5zP9QAAIABJREFU5F9QmcSFkYBACCFqiLLhgIam6wSFBBMUgrvdvYgylft5u/ggBSZmUfO8WU5fvfIc7tmnQKFQynRPRHS6G7tSmEVBhq7rJZ7yNffx5qlhkZqun1rERylcLrPWLIpXk0lAIIQQNUB5bxnouoZhMTBdJu5btzsQsNsVe7fnkJaSjypqCDCVyV9aBdKqQwjqyGHMzZtwz3eM+6YcXgdLz56YAcHsSTrO0T+dKAWmaaJboHGbUKJb+qM5C1A7dsKB/WguFyh3vlrLVtChPdnHTFK2nyQ32+kpucUf2nYPo16kFZWVhblpIxw7hnK53EFIYCBat25oWsWryoqqJwGBEELUAKWf5zVNw9DdA/B0Q3c/YZsmJrB3+3Hee2IHe3/LdkcRGtRt6M/tMzqgMjMomPM8ziVL3AsRAMrPD+2OO9B79WbbxmO89+hO0v8o8OTVsmsI4x5pg8USgHPbTvJnzYKkJCjqgtCaN8f/4Yexn3Tx7f9S+W7+IfKOuwMCi59Oj79G0K5HKBQWYP/qK1wvvww5Oe68dR09Ph5L5y5oho6myxgCX5GAQAghaoByZyosFSYoBbk5TlYs/IMd6zMpzHM3DwSGGMTdGEWny4JxfvUZzq++giNH3McYBvqVVxI4bCg5hVaWvr2LlN9yMV3uHP0DdXpd24BWncLh5HEKFn6MWrcOrcAdMKjQUIz+/bH27Mm2LSf5cXEaR/bno5R7AabmXUK4blxjwuv5UbhlM84FH6Glpnq6N7ToaKzXX4/e+C/oWYXohs6FjW4Q50teOxRCiBrgTG/na5qGywmrFx9g3eeHsee7b6t+Np0e19YnflRLjB1bKfzwQ1RmpvsgXYcOHfCfMhl7RBNWLDzI1jXHPMGALdDgioRo+l3/F/zMQvIWf4n55ZdQWOg+3mrFuPpqbCNv5M8MjS/eSuXg7jz3vV6DyCYBXDcxmqYxIajUVMz33oMdO4snHkBFRmK54w6MvpeDRZ5PfU0CAiGEqCFOFxSYLvj9tyyWvL2frPRC9z1XhxadwhlyWxsiAgvJf/99zM1b3H3/gIqMxH/MTdh69SZpQzaJHx0mN8vd1K8bGm17hTPinpZE1Ldi//VXXG++iZaRgaYUStOgVSv8JtxKfmQzvlt4iG0/HMXpKA4mdPoOr09sfH38cGFfvhyVuBItv2jcQkAAluuuw++G4WhBQRgWA70aZ1oUZcmvL4QQNcDppi5GQdqBAj5+4QD7k096BhKGRfgxcHwTOnS2kbvoE5zLlnua+rHZMP56HbYRI8jIdLLsvX2k7T3hfjtBg/pNbcSPa0yzmEDMtEMUzv8ItWvXqXEDkZH4TZyI0b0XW9ZnsnL+AQpy3cGEYdGI6V2HATf9hfC6VhwbfkZ98AEcPeourmGgdeuGZfx4iIgAiucgkFkKfUkCAiGEqCG0En/R9aIlg9HIO+Fi9eI/2fr9QVwO99O/Ldjgyhub0u+6Brh+/RHHO++gZWe7j9d1tG7dCBw3jjy/cL5+dx/b1x7D5XSHHIFhFq6+KZpe8Q0xCwvJ++RTVGIimrPozYGgIIyhQ7GNGEFKSiHL3k0lJ8NRfGqiYwIZ/vfmNG4ahNq3j8I338Lcu9fTVaA1aoR1/His7WI88yII35NOGyGEqCG8WgiKFjfQNJ2MtEKyjuXRLb6eJ1FEIxs33NmM8CA72ck70Jo3h9at3TuDgvAfeSN+vXpxYPtxjmU46Dog0nNvbtgyjPixTQmr50/+/v2YWVlo/fqd6vtvEo3/mDGYYeHsXZmKf6gfl10Xhb+/jsWm0/WqenTpVw8dF/Y9e9DDwmDwYPfJdR09NhbLwHhMv6IJjSQouChoqpJng/BMNiGEEOK0HrjyXKb8VZimC7srj3xXOo3b5zLm3mZENfajsNCk4KQDxanZAQ1DIzDEAqbCPHHC/Yph8RrDuo4eFITmZ8VeaJKX60DTNM992WI1sAUa6BqYdjuuE8dRZomyWq1ogYGg6+SddOIsVMWnBQ0s/hoWK6AUekEhmqPE8sgKTD8ryuZPcZuHu9w6KdvyWPjSERxZ9bFZI7AagWe92NHzP9SONRB8eQ+VFgIhhKgR3E0Chm7BYgZxeG8u78zajsvvCA51sspyVShMl/PMCS+Aho6fFobKq0vB8XAsmoUzv1chKpsEBEIIUYNoWDA0G3knAziQnEdegYZTr+rhYFX99K3jjx82mz+BthCs1iAMXW5P1U1+cSGEqCE0TUfXwKpsBPpFYjUCsAXUK+rb93XpLoACTTPwswRi1YKx6DZ0zXLW3QWickhAIIQQNYkCQ/dH0wwMzR9b0QuJyjPksGhWoIoOrtZ9p6cVj4wsouuGOxBAR9PkJbjqJgGBEELUEO6Bf+7me01pGLoVUJjKvSKhBqii+6umim7VWtGf6jz2FTU8VNW+kvMOaJru+S4tA74hAYEQQtRAJZ+gDU3h7ucvsdaxO1U5R57PvpKj3qtiX0XlEdVJXjsUQgghLhK+vIdKJ40QQgghJCAQQgghhAQEQgghhEACAiGEEEIgAYEQQgghkIBACCGEEEhAIIQQQggkIBBCCCEEEhAIIYQQAgkIhBBCCIEEBEIIIYRAAgIhhBBCIAGBEEIIIZCAQAghhBBIQCCEEEIIJCAQQgghBBIQCCGEEAIJCIQQQgiBBARCCCGEQAICIYQQQiABgRBCCCGoxQHB8uXL0TQNq9XKgAED+PTTTwF4/PHHGTp0KAD9+/dnzpw5XsdNmzaNG264odxz7ty5E03Tynxuv/12Vq9eTa9evQgPDycuLo6lS5dW7QUK4SPFdavk54svvmDatGlomkZYWBhDhgxh5cqVnmNeeukl2rRpQ/369RkxYgQ7duwA4PDhw/Tq1cuT7vHHH2fu3LnVfk1CiFocEACEhYWRn5/PNddcw913333B52vSpAmrVq1i1apVhIWF8cwzz7Bq1SomTpzIkCFDuOWWW9i1axcDBgxgxIgRHDhwAIC9e/fidDovOH8hLhbBwcGeurBq1Sr69u0LwDXXXMOBAwfo1asXDz74IABff/01jz/+OK+//jpr165F0zRGjx6NUoqNGzfyyy+/sH//fgCWLFnCpk2bAFBK8fvvv/vmAoW4BNXqgMDlcrFq1Sp+/fVXBg4ceMHnCwwMJC4ujri4OKxWKx07diQuLo4NGzbQtm1b/vGPfxAVFcXkyZMpLCxk3bp1pKen06pVK55//vlKuCIhLj5t2rQhIiLC8z0sLIzAwED+/PNPAObOncuECRO4+uqradmyJX/7299ISkoiPT2dzZs3A/Dpp5/yxx9/sHHjRk9A8MknnxATE8PPP/9c/RclxCXI4usCVKWCggKeeeYZNm/eTL9+/SgoKKiSfPbt20eTJk0834OCgoiJieHIkSPUr1+fF154gWHDhlVJ3kL4QkFBATNnzgTg3nvvpVGjRgB89913aJpGSEgIs2fPBiAlJYX+/ft7ju3YsSOAJyDo0qULS5YswWq10qVLFzZt2kRhYSEDBw7k6aefplu3btV8dUJcmmp1QBAUFERiYiIOh4MmTZrw/fffV0k+kZGRbNiwwfPdbrdz6NAhoqOjAbjvvvuqJF8hfCUoKIgVK1aU2T5gwABuu+02xo0b57nxR0ZGcvDgQU+aP/74A4CmTZvyyy+/8OKLL3LHHXdw/Phxpk2bxu23305SUhI9evTgkUceqZbrEULU8i6DYnv27OHYsWNomlYl57/uuuv4+eefeeedd8jIyODee+9F0zTi4uJQSvHOO++QmppaJXkLcbHQNA1d1xk7diy33347f//73yksLOS6665j3rx5rF27ll27dvHggw9yzTXXkJ+fz59//km/fv248cYbOXDgAMOHD6d79+5s3LiRY8eO8dZbb5Gfn+/rSxPiklCrA4KcnBw0TaN9+/bceeedXHvttWXSTJkyxTNS+q677gJg8eLFnm3t27c/Yz49e/Zk6tSpTJo0iaioKBYsWMA777xDeHg4Bw8eZNKkSXz44YeVfn1CXEyUUiilAHjuuefIzc3lmWee4c4776Rz58707duXtm3bkpWVxYsvvsivv/5Ks2bNiIqK4uabb2bs2LFe3QZLly7ljjvuYOPGjT6+MiEuDZoqrsGVdUJNo5JPWWMcOXKE/fv307lzZ/z9/T3bN23aRExMDIGBgT4snRC+o5Riz5495Ofn07FjR3T9zM8ipmmyfv16Lr/88moooRAXB1/eQyUgEEIIIS4SvryH1uouAyGEEEKcHQkIhBBCCCEBgRBCCCEkIBBCCCEEEhAIIYQQAgkIhBBCCEEtnrrYNE1Wr14NuCcOCgoKqtL8MjIy2L59OzabjdjY2CrNSwhfqu665XA4+OmnnwCIjY3FZrNVaX5CXKpqbQuB3W7nr3/9KzNnzuTIkSOMGTOG7Oxsz/4WLVpU6pLEycnJPPHEE9xyyy2Vdk4hLkal69Zjjz3mtZbHhAkTSExMrLT88vLymDlzJsOGDSMtLa3SziuE8FZrAwKAiIgIVqxYQfPmzTl48CATJkwoN11qaqpnqdaS9u3bx7FjxwD3Db/0aolJSUmcOHECgCuvvJJPPvmkci9AiItUybrldDrLBNzFjh8/TlJSUpmJVs5Ut/bv38+hQ4cA93LKK1asoGXLllV0NUIIqMVdBqVpmkbdunV56aWXuPfeez3bP/vsM959913Peu7vvvsu//vf//jwww9p3bo13377LZ07d6Z+/fqsWbOGDz74gDZt2jBo0CCio6PZvHkzjz76KCNGjPDVpQnhU5qm0b9/fyZMmMDixYs92w8cOMCYMWPo1asX69evJzExkc8///y0datTp0489NBDJCUlAdC5c2fPMspCiKp1yQQEANOmTePWW2/1Wps9ISGB66+/nk2bNjFkyBAcDgcA7dq1Y86cObz66qukp6fzxBNPMH36dH744Qe+++47rrvuOh588EFOnDhB9+7dJSAQl7SrrrqK5ORkXn75Zc+2Jk2a8NNPP5GUlMS2bdtYu3YtcPq65XA42LlzJ0uXLgUgLi6OPXv20KpVK59clxCXkksmIFBKYRgGH374IaNGjfIsqfr666+zcOFC+vTpg81mIzMzEwCr1QqAxWLBYnH/THXq1MFut5OUlITL5WLGjBkATJw40QdXJMTFoXiVwyeffJL4+HhP3Vq/fj3//Oc/6dmzJ4WFhWdVt7Zs2cLx48c9dWvAgAFeC4UJIarOJRMQFGvWrBn33XcfN998MwBz585l/vz5tG7dmq+++uqsFpXo3Lkzubm5TJ8+vaqLK0SN4efnx1tvvcVll10GwMcff8zEiROZNGkSd99991nVrS5dumC1WqVuCeEDl1xAADBu3DiWLFkCwPDhwxk/fjxNmjQhJCSEdevWnfH4u+66i8GDB/P9999jt9uZOnUqgwYNqupiC3HRa9OmDS+++CIAgwcPZtKkSaxbt47U1FT8/Pzo0aPHaY+/7LLL6NSpE7179yY0NJTLL7+cJ554ojqKLsQlr9Yuf1xQUEDr1q1JTU2ttjyPHj3K5Zdfzq5du6otTyGqmy/qFkD37t357LPPaNasWbXmK0R1kuWPq8jRo0eJj49n3759VZ7XDz/8wMiRI6s8HyEuBtVZt3JycoiPj2fv3r1VnpcQl7Ja20IghBBC1DTSQiCEEEIIn5KAQAghhBASEAghhBBCAgIhhBBCUIsDAtM0+f777/n+++85efKkr4tToZ07d3oWeSlWUFDApk2bKuX8DofD8zuUXkBGiPNRU+pWWloaKSkpXtukbglRsVobEJReovX555/nmmuuYdiwYaxcuRKA7777rtwV2s7V9u3b2bZtW4X7H3jggQrzefbZZ/nll1+8tqWlpXH//fdfcLlAlo4Vla903Vq+fDkDBw5k2LBhzJkzBzhznThbR48ePe1SygsXLuSbb74pd9/y5cuZO3eu1zapW0JUrNYGBHBqidawsDBeeeUVVqxYwRdffMHVV18NwJNPPlnmRn306FGvCVdKLnFcrOTSrADz589n48aN5Zbh5MmTzJ8/n4ULF3ptz8rKYv/+/V7blFJs27YNl8vltd00Tc/6CeXlD+6WhpKvqhR/l6VjRVUoufzxtGnTeO211/jiiy+YPHkyUH6dyM/PJzk52fP9bOrWxo0b+eijjyosxwcffMD//vc/r212u53t27d7bbuQupWSkuLVAiB1S9RWl8TUxcHBwRiGwapVqzzBwJYtW/jzzz/573//y+DBg3E4HDzxxBO0bNmSPn36MGnSpHKXOC69NOvUqVNZt24du3btwjTNMgsdzZ8/n/vvv58PP/yQO++8E4C1a9dyzz33EBsbS2JiomdCo7Fjx2IYhlczbP/+/YmKiqJOnTo8/fTT/Oc///HKf+bMmcTHx9O2bVucTievv/661/e33367yn9fcWlr1aoVn332GVOmTEHXdbKzs73qxJgxY2jZsiV9+/alWbNmzJgx46zq1uzZs1m6dClbt25lxowZTJs2DV0/9QyzZ88eQkNDSUlJITs7m/DwcDIyMhg+fDidO3dm06ZNXHPNNcD51a3Zs2fz8MMP88cff5CZmcnnn3/O0KFDpW6J2ktVsio45XnJz89XjRs39nxPSkpS/fv3V8OGDVNHjhxRSil1xRVXqH379imllFq2bJm65pprPOnnzJmj/vOf/yillDp+/Lhq1aqV2rhxo7r++us9aa688kq1e/du9a9//Uu999575ZajR48e6tixY2rgwIFqx44dnnxPnDihlFJq0qRJatmyZerTTz9VM2fOVEoptW/fPnXFFVd40q5cuVIppcrN/9dff1WNGzdWycnJSimlMjMzvb4X69atm+dahbgQpetWRkaGmjRpkurQoYPauHGjUkp51Yn8/HwVGhqqTp48qZQ6t7q1bNkyNWnSpHLLMWXKFPXll1+qmTNnqldffVUppdT999+vVq9erZRS6r333lP/+te/zrtu7d69W8XGxqoFCxYopaRuierhy3tore4yKKljx44kJibSr18/7r333nLTNG3a1PP3rVu3kpSUxIwZM3jhhReYOHGi19KsM2bMOOPSrD///DOGYfDbb7/Rtm1b5s2bR3p6OpmZmQQHB3ulXbVqVYVztDdv3hyg3PyjoqJ49913uf3223nuueeoW7eu13chqlpERARvv/02L7/8MkOHDi03Tf369QkMDAQqp24VFBQwf/58AgICiIqKYv78+QCsXLmSFi1aeKU937rl7+/v6ZKYNGkSNptN6pao1S6JLgOA3NxcgoOD6devn2dQocViIS8vr9z05S1xvHHjxnKXZrVYLJ414Et666236NChA2vWrCEiIoJ3332Xxx9/nPT0dI4ePUpERIQnbbNmzdiwYQNjx46t8BoqWhrWarWyYsUKWrRowS233ELHjh29vkdGRp75BxLiPBXXrcsvvxyXy4VpmhXWCTi3urVr165yz7Nw4UI6dOjgWZ306NGj7Nixg+bNm7N+/XpGjBjhSXshdevQoUN8+eWXXH/99SxdupS+fftK3RK11iURELhcLvr370+9evXIycnhlVdeAWDgwIEkJCQQFxfHDTfc4HVMRUscl7c065VXXsltt93Gp59+yueff05wcDDHjx/nyy+/ZP/+/QQEBADusQPfffcdjz/+ODExMXTu3Jn09HRGjhzJhAkTuPLKK+natSuNGzcu9zrKWxp28uTJtG/fnk6dOtGxY0cMw6Bt27ae73Xr1q3aH1dc8u6++24OHjxIVlYWs2bNQtd1rzpRelDgudStLl268NNPPxEfH89jjz1Gv379AJg7dy5PPvkkcXFxABiGwfvvv89DDz3EiBEjeO655zAMg/79+5933XriiSe45ZZbyMzMRNM0YmJivOqa1C1R29TaxY1KL9HqcDjYtm0bHTp0wM/Pz8elq36ydKyoLOUtf7xt2zaio6MJCwvzYcl8Q+qWqEyyuFEVKblEq9VqpVu3bpdcMCBLx4qqUHr5444dO15ywYDULVHb1NoWAiGEEKKmkRYCIYQQQviUBARCCCGEkIBACCGEELX4tUPTNFm9ejUAPXv2JCgoyMcl8r3du3dz8OBBGjRoQExMjK+LI2ooqVtlZWRksH37dmw2G7Gxsb4ujhDnpda2EJReka2wsJD777+fPn36EBcX55mH/J577vH843Y+qx/m5+dzxx13cO211zJo0CCys7PPuEIbnHkVtzMJCgpC0zTPZ9euXQBcccUVFS5Ju2zZMh544AGeffbZ885XiNJ1C+C1114jNjaWAQMG8I9//AOAjz/+mFmzZgGVV7dM0yyzUFhpZ5PmdK6//nqvuvXOO+8Ap69bycnJnnkLhKixKnsu5Co45XkpPd/61KlT1SOPPHLaY0qubXC2PvvsMzVhwgSvbeXNv37gwAGVmpp62jR2u11t2rRJ2e32s87/m2++UTfffHOF+3/77TeVn5/v+b5o0aIK54YX4myUrltLly5V8fHxKi8vr8JjKqtu5efnq9atW3ttK123yktzPnXr6NGjql27dqqwsLDc/aXrVkZGRpl8hThXvryH1toWgtLmzp3Lv/71L69ty5cvp1GjRvz4449eqx+uXLmSa6+91rOe+4IFC5gyZUq5523RogWbN28mJSXFs63kCm2mafLZZ5/x97//nenTp3tWQyydJjk5mR49evDaa6/Ro0cPduzYcVbX9eijj3qu6z//+Q8hISGAeza4nj178uKLL3pWfhOiKrz11ltMnjzZMyMnQFpaGh06dODpp58uU7dmzZrFf//7XwCOHDlCr169yj1veXXr66+/JisrixkzZpCSklJu3Sqd5nzr1qxZs7j//vvx8/OTuiUuDZUdYVTBKc9LyaeY9PR0ryeakm666Sa1Zs0apZT3U8xHH32k7r33XqWUUsOGDVObN2+uMK8lS5aoFi1aqEceeUTZ7fYKn/7Xr1+vIiIiyk1zww03qPXr1yullPr222/VLbfccsZr/OSTT8q0DhRf55133qmWLFmilHK3IowaNUopJS0E4sKVbiHo0qWLZyXPkl555RU1a9YspZR33Tp06JDq1KmTUkqpZ599Vr344osV5lW6blX09F+ybpVOcz51KzU1VbVs2dKrdeBMdUtaCERl8OU99JJoIahbty5//vknOTk5Z33MiBEj+PLLL0lLS+PAgQN07dq1wrSDBw9m8+bN7N+/n7feeqvM/tdff52BAwfyxRdfYLPZyMzMLJPmt99+Y8mSJcyYMYP169cTHx9/2vKZpsn06dPLtHqUJzo6moMH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gCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgSVVHH0Bbq1QqHX0IAHSQUkpHH0Kn1eWCIPGCAE5vlUrFONUO/A/hyXHLAAAQBHReBw4cSKVSSaVSydChQ3PPPffk4MGDbb6f7373u/n85z/f6jLLly/P5s2bkyTNzc0599xzs2XLljY/FvhfHT58OOeee2527dp1Svf70ksvZd68eTl8+PAp3S//f4KATu9Pf/pT5s+fn8ceeywzZsxomb537940Njbm0KFDLdMOHTqU1atXH7V+U1NTnn766TQ3Nx81ffXq1XnppZcyceLETJ8+vWV6KSUrV648Kj5uv/32/O1vf0uSVFdX58EHH8zAgQNbll+1alVefPHFo7Z/ZHpTU9NJXgE4sUceeSTr16/PggULjpm3fv367N27N0myZs2ao34GTvS6PZGXX345q1atarkVsnnz5kyZMkUQdCaljbXDJjvV/jl19u/fX5KUJ598spRSyvz580uPHj3KoUOHygMPPFD69u1bRo0aVc4777yyfv36sm7dunL22WeXiy++uHzmM58ppZSyYMGCUltbWxoaGsrAgQNLc3NzGT58eGloaCjDhw8v3/zmN0uSMnHixFJKKb179y7vfOc7S319fRk8eHBZu3ZtmTdvXqmpqSmXXHJJ+frXv14+/vGPlyRl1apVZfv27eXiiy8ugwYNKjU1NWXOnDmllFKGDx9eLr/88nLRRReVurq68tRTT3XMRaRDnMpx6rrrrivXXXddaWhoaJl2xx13lMGDB5cRI0aU7t27l3e9611l5MiRZcCAAaWxsbHV1+3SpUtLKaVMnTq1TJ48ufz9738v9fX15cILLyzDhg0rDQ0NZd++feW2224rScr48ePLww8/fErOtSuM/x15DoKATuv/BsGTTz5ZkpRNmzaV6urqsmzZslJKKZ/97GfLLbfcUh566KHSv3//8uc//7mUUsqePXtKdXV1Wbx4cSmllB07dpRSXhn0pk2b1rKfWbNmHRUEjzzySNm/f3/54Ac/WD71qU+VUkrp379/efTRR486rlWrVpW77767XHnllaWpqaksXbq0VFVVlZ07d5bhw4eX73//+6WUUiZOnFhmzpzZ3peL08ipGqe2bdtWqqqqytatW0ttbW357W9/W0p5JQhuueWWUkopH/7wh8vnPve5UkopY8aMKXPmzGn1dXu8IEhS1q5dWw4fPlze+ta3lp///Odl1apVJUlpbm4+JedaStcY/zvyHNwyoMtobGxMz549c+jQoTQ3N2fu3Lm5+uqrs27duvTt2zfXXnttbrzxxowdOzZTp07Ntm3b0tzcnPe9731Jkn79+rVsa8yYMSfczxlnnJHu3btn9OjR2bZtW6vH9I9//CMjR45MTU1Nrrzyyhw8eLBlnbe85S1Jkvr6+rzwwgsnefZwrIULF2bgwIHZtGlTLrzwwvzoRz9qmdenT58kSa9evVJbW5vklZ+BAwcOtPq6PZF+/fqlUqmkvr4+//rXv9rvpGg3goAu4fHHH8/s2bNz55135uyzz05dXV0mT56cX/7yl1m8eHHmzJmTmpqafPnLX87DDz+c+++/P29+85tTV1eXJUuWpKmpKTt27Hhd+9q3b19efvnl/PGPf8yIESOSJFVVVdm9e3fL/dgjzj///DzxxBNpbm7O008/ndra2gwaNKitTx+OUUrJvHnzMnr06KxYsSKjRo3KkiVLXtc/1id63Z511lkt8bp9+/ZWt1FV9cq32o/3c8HpSRDQ6V122WW544478slPfjLf+ta30r1798yePTtTpkxJpVLJu9/97uzfvz+PP/54Bg4cmClTpmTChAkZMGBAZs+enVmzZqV79+65+uqrX9cH/D7wgQ+kW7dueeaZZ1q+fXDNNdfkox/9aK644oqjlr3tttty4MCBDBs2LB/5yEdy7733pkePHu1yHeDVfvWrX2XLli257777cs8992TOnDnp379/Fi5c+Jrrnuh1e+ONN2bWrFnp1q1bVq5c2eo2hgwZkgsuuCB9+/bNV77ylbY6LdpR5T/3LNpugx38Czc6ev90bXV1dfnZz36Wyy+/vKMPhU7MONU+usJ17chz8A4BAOAdAoBTzTjVPrrCdfUOAQDQobrkw4084AI43RmnON10ySDo7G8ZAV1bV3hr+3Qksk6OWwYAgCAAAAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAAEQQAQAQBABBBAABEEAAASao6+gDaQ6VS6ehDAGiVcYrTTZcMglJKRx8CwAlVKhXjVDsQWSfHLQMAQBAAAIIAAEgX/QxBkjQ2Nmb58uXHnTdkyJDcdNNNWbp0aZYvX55du3Zl5MiRmT59empra1/XuqWU3HDDDVm0aFG6devWMv/+++/P2972tnzsYx9rl/MC6Gy2b9+eb3zjG9m6dWtGjRqVGTNmpKam5oTLL1q0KMuXL0+vXr1y6623ZsyYMa9rHienywbBs88+m9/97ndJkrVr12bnzp257LLLkiT79u3L3LlzM23atEyYMCGDBg3KD3/4wyxbtix/+MMfXnPdJPnFL36Rhx56KBMmTMikSZNa9vud73wnl156qSAASLJ///6MHz8+STJ27Nh87Wtfy4YNG7Jw4cLjLj9//vxMnjw5t956a1auXJmrrroqjY2NOf/881udRxsobawdNnnS+7/55pvL0KFDW/7+3HPPld69e5dJkya1TNuyZUuprq4u9957b6vrHnHDDTeUJOX973//UdOHDh1abr755pM9DaAL6+hx8lSaPXt2qaqqKtu2bSullPKlL32pdO/evezcufOYZfft21f69etX7rrrrlJKKS+88EKprq4ud999d6vzjugK17Ujz+EN+RmCRx99NLt27cpdd93VMu2cc87JuHHjTnir4NWef/75LFu2LJMmTcqvf/3rrF+/vj0PF6DTeuKJJzJ8+PAMGDAgSXLppZfmwIED+ctf/nLMsuvWrctzzz3XchugT58+GTFiRNasWdPqPNrGGzIINm/enCR5xzvecdT0/v37Z9OmTa+5/o9//ONUV1dn7ty56dOnT+bNm9cuxwnQ2T377LPp06dPy9+P/HnHjh3HXfbVyxz5844dO1qdR9t4QwbBGWeckSTZs2fPUdN37tyZM8888zXXX7BgQa699tqceeaZuf7667No0aI0Nze3y7ECdGa9e/du+exV8t9xt1evXsddNskxy9fW1rY6j7bxhgyCIx9AWbFiRcu0PXv25Pe//30uuuiiVtddsWJFVq9enSVLlqRSqeQHP/hB/vnPf+anP/1pux4zQGd01llnZePGjTl8+HCS/75De9555x2zbH19fZJk48aNLdM2btyYYcOGtTqPttFlv2XQmnHjxuWCCy7I9OnTs3v37vTu3Tvf+9738vzzz+eLX/xiq+s+8MADqaury09+8pOWX5M5efLkLFy4MNdff32SV94K+81vftOyzjnnnJMhQ4a02/kAnK4+9KEPZf78+Zk2bVq+8IUvZPHixbnmmmsyYMCA7N69O1OnTs173vOefPrTn87gwYNzySWX5Nvf/nauuuqqPPXUU3nxxRdz0003tTqPNtLWn1Jsh02e9P6P902Bv/71r2XIkCElSUlSevbsWR588MFW1927d2/p2bNn+cQnPnHUMjNnzixvetObyjPPPFOGDh3ass0j/82aNavtThDo9Dp6nDzV7rvvvlJXV1eSlIEDB5YNGzaUUkpZu3ZtSVJGjx7dsuzWrVvL+PHjW8bPV3+LoLV5pXSN69qR51D5zwG0mY5+aMf/sv+DBw9m/fr1OXDgQBoaGo76BUMA7aWjx8mO0NTUlDVr1mTEiBFH/VKixsbG1NfXt3wL4YgNGzakR48eefvb337Mtk40rytc1448hzd0EAB0BONU++gK17Ujz+EN+aFCAOBoXfJDhZ6JDZzujFOcbrpcEHT2t4sAoCO4ZQAACAIAQBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAECSqvbYaKVSaY/NAgDtpM2DoJTS1psEANqZWwYAgCAAAAQBABBBAABEEAAAEQQAQAQBABBBAAAk+TfASBETqTACzQAAAABJRU5ErkJggg==", }, { - name: "Mobile-Tax-Invoice", - template_id: 1002, + name: "Web-Invoice-2", + template_id: 3002, ImageUri: - "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vXr1qFixIvfddx979uwBYOLEifTu3RuA+Ph4Bg0aRPny5WndujVz5swhIyPDa7KQUorp06d71W38+PEopShdujSRkZFs27bNa//MmTMZN24cAI888kie6yml2Ldv3wXLEUJcidyLC4NpakytQNmuyY/GClpjmgrTNC7/xzN8IAGByOuGDwhyOnDgAIMGDfLqWgsNDeW+++5jwYIFAJw6dYply5YxZMgQfvrpJyZOnMh//vMf1q1bh1KKBx98ME/X3IABAzh9+jSbN2/m448/plWrVp59M2bMYNWqVaxatYo+ffrkqdO9995LTEwMd911F127duXIkSOefZ9//jlffPEFDoeDF154gVWrVvH000/TuHFjzzUrVqzosxytNfv27SvUz0+Iksn3xL+8YcLV45oPYKEwGnLX5EMJCEReJSogAFi5ciVvvPGG17aHH36YH374gfT0dObNm0ezZs1o2bIls2bNYsiQIXTr1o1bbrmF4cOHs2PHDuLi4jzn/v3336xYsYIvvviCmjVr0q5dO5o0aZKn3Jo1a1K1alWfdSpfvjzPPPMMZcuWZe7cuQDs2LGD6OhowsPDWbhwIQ0bNqRr167Url2b4OBgunbtSteuXQkICPBZznfffUd4eDgbN2680o9MiBIunwWIrmGbqrUmPS3dtdTwlZJYQOSjxMwhcI+ZTZs2jccff5wKFSp4tvfo0YPg4GCioqKYP38+Q4cOBSA6OprOnTt7rtG4cWMAr4DgwIEDhIaGUqlSJZ/lzp49m6CgIAYNGkTNmjULrGPz5s091541axa9e/fmlltuYdasWTz44IMFnpu7nB49ejB58mRatGhR4HlCiAtQORYHyn60Tymw2cBiyV4KOHueX/Z8Qc+/Xgqa85frXEOB1xOMWmG1lMbpTMH0zCG4dFar62+e1U8ePRR5lZiAwN3N36FDB9555x3Gjh3r2W6xWBg0aBDvvvsuO3fuZODAgQBUqFCB2NhYzzX++ecfAGrUqOHZVrFiRZKSkoiLi/N03+c0bdo0WrdufVH1++uvv4iIiCA9PZ0vvviCMWPGUKZMGZYvX87+/fupV69evufnLicsLMwz/0AIcQVyTSp0fbnQBAZorDYDpa5+diCtNYF2G84sA6fz8r/iK6WwGIrAIAsWqwZZn0jkUGJixJzP5j799NOeCYFuQ4cOZd26dfTr14+w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yXCByEECAiGEuEiumfiuxtWicn7rdrfqvr/xZ5/tY/vlnEMB+y50vZznSc+A8CaPHQohhBDXUHFt86S/SAghhBASEAghhBBCAgIhhBBCIAGBEEIIIZCAQAghhBBIQCCEEEIIJCAQQgghBBIQCCGEEAIJCIQQQgiBBARCCCGEQAICIYQQQiABgRBCCCGQgEAIIYQQSEAghBBCCCQgEEIIIQQSEAghhBACCQiEEEIIgQQEQgghhEACAiGEEEIgAYEQQgghkIBACCGEEEhAIIQQQghKQEDQuXNnlFJUq1aNJ554gsTERACCg4P5888/Wbp0KWFhYT7Pe//9931e85FHHkEpledn7969PPXUU1SsWJE6derw5JNPesoToiQaP358nvskMTHR674cNGgQR44cASA9PT3fe2j+/Pk8++yznmt3796dAwcOFMn7EuJGdMMHBADvvfceGzduJCoqiq+//hqAlJQUtNaXdb0XXniBVatW8fTTT9O4cWNWrVrFqlWr+Oyzz9i4cSMbNmxgzpw5/P7777z00ksAZGRkEBMTU2jvSYjrRefOnT33yKpVqwgKCgLgtdde448//iA1NZWpU6cCMHHixHzvoa1bt7Jw4UIAzpw5w+rVq9myZQsg95cQhaFEBASHDx9mxYoVZGRk0K5duyu+XsOGDenatSu1a9cmODiYrl270rVrV2bPns348eOpVasWHTt25J577mHNmjUAPPfcczRt2vSKyxbieta2bVusVqvndeXKlbFarcTGxgIwY8aMfO+hbdu2ERMTw+bNm1m8eDGmabJ161ZA7i8hCkOJCAhWrFjBRx99hN1uJyEh4aqUcerUKRITE7n55ps92xo1asTJkycBGDZsGB999NFVKVuI4mzfvn288cYbvPHGG8THx3u2v/LKK/j7+7Np0yZGjx59wXto8+bNNGvWjMWLF/Pzzz/TrFkztm3bBsj9JURhKBEBwfDhw9myZQsvv/wyr7322lUpo2zZsgCebzoAMTEx1KhRA4DmzZvz6KOPXpWyhSjOIiIiWL58OcuXL/dq7MeNG8fo0aPJysqicePGBd5D+/fv59y5c0yZMoUFCxawfPlyXn/9dTZv3oxpmnJ/CVEISkRAAOB0Ovnrr79QSl2V61utVvr06cPkyZM5duyYp1eid+/eAOzZs4dvvvnmqpQtxPVGKUXp0qV55513CA0N5dlnny3wHtq6dSuNGzemR48eOJ1Ounbtyh133EFKSgr79u2T+0uIQlAiAgL3H5s1a9Z4Ji/llJSU5DUL+tixY57z3NtGjhx5wXImT57M8ePHqV69OpGRkbRu3ZoxY8YA8OGHHzJ06NDLnsgoxI1Ea43WGj8/P7744gu++OILfvvtt3zvoS1bttC8eXMAHn30UQYNGoTNZqNJkyZs2bJF7i8hCoHSxeAOUkrdMDdyVlYWO3bsoHz58l7dowkJCZw8eZKGDRsWYe2EKP7yu4cKIveXuJ4U1zZPAgIhhBDiGiqubV6JGDIQQgghRMEkIBBCCCGEBARCCCGEkIBACCGEEEhAIIQQQggkIBBCCCEEYL3wIde36Ohojhw5QoUKFWjUqNFVL2/Tpk2kpqYSHh5OpUqVrnp5QhRnCQkJbN++HZvNRocOHa56eQcOHCA2NpZKlSoRHh5+1csT4kZyw/cQzJo1i6eeeoqFCxeydOlS3n33Xc++2bNnM2HChEItb8aMGQwdOpSlS5cW6nWFuB5t2rSJhx9+mA8++ICMjAzuvfdez76DBw/SrVu3Qi0vKiqKsWPHet3nQoiLc8MHBACjR4/mlVdeAWD8+PH8+eefeY7Jyspi27Zt2O12r+12u53t27djmiapqakcOHDAa39SUhI7d+70vP7ss8/o06fPVXgXQlyfunXrxoIFCwBYtGgR77//vs/jdu7cSVJSks/tGRkZOJ1OduzY4bWgi/u+zcrKAmDUqFG89NJLV+FdCHHju+GHDHJSSnHHHXfwxBNPsGLFCs920zTp27cvderUYf369UyfPp1WrVrRrl07mjZtSlpaGv/88w8333wzmZmZVKxYkenTp7NgwQImTZpEixYtiI2NZcmSJfj7+xfhOxSi+FJKUadOHZYsWULHjh092Q3Blfnw9OnTHDlyhAceeIARI0YwcuRIUlNTCQsLY8WKFTRv3pyyZcuyZcsWVq9eTXR0NAMGDODWW29l48aNzJ8/nwYNGhThOxTi+lYieghyqlixIq+99hpDhgwBXH+kDMNgyZIlPP/883Ts2JFFixYBcO7cOd5++22++uorDh06xNy5c/nyyy/54YcfsNvtTJgwgfXr1zNnzhzatWvHV199VYTvTIjizzAMvvjiC0aMGEFKSoon++hbb73F1KlTGTx4MPPmzfMcf9999zFt2jSaN2/O//7v//LJJ58QEBDA/v37eemll5gxYwYzZszg3Xff5a233iqqtyXEDaFE9RC4M6zdfffdrFixgtmzZ9O9e3fi4+MZOHAg1apVIysri9DQUM85NpsNcKU3dv9YLBaOHj1Kamoq7733HgB+fn7Uq1evSN6XENcD9/1XvXp1Jk6cyFNPPYWfnx8Aw4YNIyEhgfr163PmzBnPOTnvP4vFAkCZMmWw2+38/fffLFmyhGXLlgEQGRl5jd+REDeWEhUQ5PTmm2/Spk0bANauXUvlypWZPXs28+fP5/fff7/g+TfffDP+/v6MHTuWoKCgq11dIW4o9957L1FRUezfv5+zZ8+yfPlyjhw5wokTJ/jpp58u6hrNmjWje/fudO3a9epWVogSosQGBAEBAcybN49FixbRqVMnRo0axfDhw0lKSuKff/654Pk2m41XX32ViIgIKlWqRGBgID/++OPVr7gQN4j33nuPp556ijJlylC/fn3uv/9+tNZkZGRw4sSJC54/adIkBg0aRPny5UlOTmbRokVUrFjxGtRciBvTDZ/+ePz48dSoUYMRI0Zclev7MmbMGFq0aMGjjz56zcoUojhaunQpX331FV9++eU1K3PhwoX88ssvzJw585qVKcSlkPTHReijjz7itddeuyZlDR8+3DMpUQgBK1eu5P77778mZU2bNo3XX3/9mpQlxI3mhu8hEEIIIYqT4trmlYgeAiGEEEIUTAICIYQQQkhAIIQQQogSEBBER0ezevVqdu3aVdRVyVdGRgZbt27Ns33v3r0kJCQUShkHDhxg9erV7N27t1CuJ8TFSEhIYPXq1fzxxx9FXZUCbdiwAdM0vbadPHmS6OjoQrn+6dOnWb16NRs2bCiU6wlxNdzwAUHObIcHDx6kX79+3H333Tz22GMAxMfH8+uvv15xOaZp8s033+S7/+TJk7zwwgv57hszZkye7e+++y6bNm264rqBZIETRSNntsP09HRGjBjBHXfcwd13301iYuIF75tLsWLFChITE/Pd/8gjj+S7b/DgwXkSmy1dupRZs2YVSt12797Nq6++yuDBgwvlekJcDTd8QADnsx1Onz6dXr168fPPP/Ppp58CsGXLFq+10932799PSkoKkDejIeTNsma323n55ZfzrcPMmTP59ttvcTqdnm1aa3bu3Om1DeDs2bPExMTkuUZ8fDxHjx71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lMI3n07WrVu3rFmzJv/85z+z7777JkkGDBiQu+66K3379t1suVWrVm3zEcVHHXXUzt40aPYOOuignH766bnhhhvSu3fvTJ8+fbP9au3atVm+fHkGDx6cZNN3+m8+PrxNmzZJko4dO+bVV1/d5iOKPdYb/jstIgiOOeaY9O/ff5vLHHrooXn88cdTW1ubRYsWpW/fvltcbmuPKE423UNwwAEHbJcxw+7i4IMPzsknn7zNZfr06ZPFixfn1FNPzcKFC1NTU7PF5bb2iOJk0z0E+++/f/bYo0VdCYXtpkUEwbtx7bXX5lOf+lT69OmTl19+OXPmzMm99977tuX+/0cUV1VVpba2Nt/4xjeyYMGCHHvssXnllVfygQ98YBdsAbx/jRkzJkOGDMnMmTOzatWqppt/36qysrLpEcXdu3dP27ZtM3369CSbYn3SpEk566yzdubQYbfh4UYAsBM113Oen60BAIIAABAEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAARBAAABEEAEAEAQAQQQAAJGm1qwewo1RUVOzqIQCwGyil7Ooh7BS7bRAkLed/IvDuVFRUNMvjQnMd15a8n8a6PbSkby5dMgAABAEAIAjYhd54441UVFSkoqIihxxySK677rps2LBhu7/Oj370o3zxi1/c6vx169ZlwoQJ2bhxY5KkoaEhhx56aP7xj39s97HQPN17772prq5u+vucOXM2+3tzM3jw4FRUVKRnz54577zz8vzzz2912fnz5+eBBx7YiaPbugsuuCB33333Tn/dGTNm5Lnnntvpr/t+IwjY5f785z9n4sSJmTt3bq666qqm6a+//noeffTRNDY2Nk1rbGzME088sdn69fX1WbhwYRoaGpqmbdiwIY899lgaGxszcuTIXHnllU3zSil5/PHHm+Ljueeey+jRo5uCoLKyMr/4xS/Sq1evpuUXL16cf/3rX5u97pvT6+vrt9NXguaqvr4+ixYtetf/r9esWZMlS5Yk2fJ7dnv45je/mQcffDDr1q3LjTfemGRTZC9cuDCvvPJK03Ljx4/P7NmzN1t3S/vWjvbaa69l6tSpmTBhwtvmrVixIitWrEiSPP/883n11Vc3m//888/n6aefflevs6X98rOf/WyeffbZ/2L0LURpBnbEMJrJprENdXV1JUmZP39+KaWUiRMnlrZt25bGxsYyefLk0qlTp3L00UeXvn37lqeffro89dRTZf/99y9HHXVU+fznP19KKWXSpEmlqqqqHHbYYaVXr17lueeeK0nKEUccUfr161fuueeekqSMHDmylFJKhw4dysEHH1y6detWDjjggLJkyZIyZsyYkqQMGTKkTJ8+vQwfPrwkKYsXLy4rVqwoRx11VOndu3dp3bp1GT9+fCmllJqamnLiiSeWgQMHlurq6rJgwYJd80XkPdnacWHWrFmlffv2Ze7cuWXu3Lll/PjxpUOHDqWUUu6///7SvXv3ctBBB5UuXbqU++67ryxbtqwkKatWrSqllDJw4MAyefLkctttt5Xu3buXmpqaMnjw4PLkk0++7T37Xsa1NYMGDSrf/OY3S319fRk+fHg5++yzSymlXH/99eXYY48tHTp0KF/60pfKggULSo8ePcrBBx/ctMyW9q334j89tt50003ljDPOKO3bty8vvvhiKaWU2267rey3336lb9++pXXr1uXYY48tNTU1paqqqkycOLFs3LixjBo1qlRVVZXOnTuXs88+u2zcuLFccsklZezYsaWUUqZNm1YOOeSQUsqW98sJEyaU1q1bl2OOOab8z//8z3sed0s6PzWLUbWkLzj/561BMH/+/JKkPPPMM6WysrLceeedpZRSLrvssnLBBReUqVOnlu7du5dHHnmklFLK2rVrS2VlZZkyZUoppZSXXnqp6UD95rqllPLVr351syCYNWtWqaurK0OHDi0XXnhhWbx4cUlSGhoaNhvX4sWLy9VXX11OOumkUl9fX6ZNm1ZatWpVXn311VJTU1N+8pOflFJKGTlyZPnKV76yc75o/Fe2FQStWrUqQ4YMKUOGDCnHHHNMUxDU1taWK6+8spRSyuWXX15qa2u3GQRVVVVl+fLlpZTytvfsex3X1gwaNKgkKUlKr169ygMPPNA077XXXivjx48vVVVVpZRN78/rr7++lPJ/+8xb96334j89th522GHl5z//efnEJz5RvvWtb5VSNgVBnz59Sn19ffnud79bjjjiiNLY2FiuueaactZZZ5WZM2eW6urq8sILL5QXX3yxVFZWlpkzZ24zCLa0X3bv3r387ne/+4/G3ZLOTy4Z0Gw8+uij2XvvvdPY2JiGhobceOON+ehHP5qnnnoqnTp1yqmnnppzzz03gwYNytixY7N8+fI0NDTk5JNPTpJ06dKl6d+qra3d6uu0a9cubdq0yQknnJDly5dvc0wvvPBCDj/88LRu3TonnXRSNmzY0LRO586dkyTdunXL6tWr/8utZ1dr165dZs+endmzZ+c73/lO0/Rly5blQx/6UJJN1+7f6d6Sgw46KD169EiSt71nt6drrrkml19+eRoaGtK/f/80Njbm7LPPzplnnpnnnnsua9asybp16zZbZ8WKFVvct3a0efPm5S9/+Uvat2+f3r17Z8KECU2XK9q1a5fWrVtnn332SVVVVfbYY4906dIldXV1eeGFF9KrV6/su+++6d69e/r3759ly5Zt87Xsl/85QUCzMG/evIwbNy6XXnpp9t9//1RXV+fiiy/O7NmzM2XKlIwfPz6tW7fOtddem+nTp+emm25K+/btU11dnTvuuCP19fV56aWX3tVrrV+/Pv/+97/z0EMPpX///mnVatPHcaxZsyavv/76Zsv269cvDz/8cBoaGrJw4cJUVVWld+/e23vzacYOO+ywppvyFi1alP79+6djx45p3bp1XnnlldTV1b3tmveb3vqe3V4nqIqKirRr1y7jxo1Lhw4d8uUvfzlLly7N1KlTc/vtt+eEE05Isun+hVatWmXt2rWpr69Pt27dtrhv7WiTJk3K4MGDs3jx4nTt2jVr167NrFmz3nG9mpqaLFmyJCtWrMiqVauydOnS9OvXL126dGm6T+LNew+2pVWrVlvcv9ncbv3BRLw/1NbW5tBDD82oUaNy1VVXZc8998y4ceMyevTonHfeeenVq1eefPLJLFiwIKecckq6du2a0047LT169Mi4cePyhS98IVdccUUOP/zw/OY3v3nH1/v4xz+eZNPJ/uabb07Xrl0zYMCAdOrUKWPHjt3su8MxY8bkjjvuSJ8+fbJy5cqMHz8+bdu23WFfC5qf6667LmeeeWbuvvvuvPbaa/ntb3+bdu3aZdSoUTnqqKNSXV3ddEPqW82bN2+z92zHjh23y5jKpsu9ad26dX7605+mtrY2I0eOzHHHHZfBgwdnr732SocOHbJgwYIMGTIko0ePzve///2sWrVqi/vWjnxPr1u3LlOmTMntt9+eM888M8mmG3lvvfXWDB8+fJvrnnjiiRkxYkRqampSWVmZoUOHZtCgQWnTpk3OPffcVFRU5IgjjnjHMZxyyin55Cc/mYEDB2bRokXbZbt2RxWl7PqPnNoRn3zV0j5Ni3enuro6d911V0488cRdPRR2geZ6XGiu49qS99NYt4eWdH5yyQAA8BMCoOVorseF5jquLXk/jXV7aEnnp936HoKW9FAK4N1prseF5jquLXk/jZV3b7cOguZYYMCu01y/M2uu49qS99NYt4eWFD/uIQAABAEAIAgAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAIggAgAgCACCCAACIIAAAkrTa1QPYkSoqKnb1EIBmprkeF5rruLbk/TRW3r3dOghKKbt6CAC7lYqKihZ1bG1J8eOSAQAgCACA3fySQZLcdNNNee2117Y478ILL0yPHj3yve99L/Pnz89ee+2V448/PpdddlkqKirecd2ePXtm2rRpWbNmTS666KKmeY8++mhmzJiRz33uc+ncufMO2S6AlmzDhg359re/nUWLFuWAAw7IVVddlR49emx1+Xnz5uWWW25JXV1dhg4dms985jPval5LstsHwfz587NixYrU1dXlwQcfzIABA9KtW7ckybBhwzJ27Njcc889Of/887Nu3bpcccUVWbhwYSZNmvSO6ybJD37wg6xcuTIXXnhh07Wmxx57LNdff31GjBghCAB2gNGjR2fmzJk555xzMnXq1MyYMSN//etf06rV209rjz/+eIYOHZrjjjsu++yzT0aNGpUNGzbk4osv3ua8Fqc0AztiGG/9N5988smSpEyePLlp2qxZs0qScssttzRNu+OOO0qS8qc//Wmb65ZSylNPPVWSlCRlzpw5TdMnT55ckpQnn3xye28WwC7VHE4bDz/8cElSfvnLX5ZSSpkxY0ZJUmbOnLnF5U8//fRy5JFHlsbGxlJKKccff3ypra19x3ml7JzzU3PRou8hmD59ejp16rTZj4eGDx+etm3bZvr06e+4/s0335x+/fqltrY2kyZN2pFDBeD/eeihh5IkgwYNSpIcf/zxSZInnnhii8v/8Y9/zIc//OHsscemU95xxx2XRx555B3ntTQtOgieffbZ9O7dO5WVlU3T9txzz/To0SNLly7d5roNDQ352c9+lhEjRmTYsGGZNm3aVu83AGD7WblyZZKkY8eOm/33pZdeetuyb7zxRl599dVUV1c3Tauurk5dXV1Wrly51Xkt8XjeooOgffv2Wbt27dumr1q1arM3yJb86le/yssvv5zhw4dnxIgReeONN/yUAGAn6NChQ5Jk/fr1SdJ0HK+qqnrbsm3atEnbtm2bln1z+T322COdOnXa6rx27drtyE1ollp0ENTU1OTpp5/OsmXLmqbdf//9Wbt2bQYOHLjNdSdPnpwkGTBgQD74wQ8mSSZMmLDDxgrAJl27dk2S/O1vf0uy6ae9SdKnT58tLr/ffvvlmWeeafr70qVL069fv7Rq1Wqb81qaFh0EY8aMSZs2bTJs2LBMnTo1P/zhD3POOeekZ8+eOf/887e63rPPPpt77703l19+eebOnZu5c+fm61//ep544onMmzevabn58+fn97//fdOfjRs37ozNAtitfexjH8vee++dq6++OkuXLs2Pf/zjdO7cOWeccUaS5IYbbshll12WDRs2JElOO+203H333ZkyZUoeeOCB3Hffffn0pz/9jvNanF19V2Mpu+63DEop5c477yx77713028LHHjggeXRRx/d5rrXXXddSVL+/ve/Ny2zdu3a0qZNm3LRRRc1/ZbBW//U1dVt9+0E2JmayWmj/OEPfyiHHHJISVIqKyvLnXfe2TSvZ8+epbKysqxevbqUUsr69evLpZdeWiorK0uS8pGPfKSsW7fuHeeV0rJ+y6CilF3/odQ74rOx38u/uX79+ixZsiQdOnTIgQcemD333HO7jgVgd9GcnmVQSsljjz2WAw88cLP7B5YuXZq6urrU1NRstvzq1auzYsWK1NTUvO0ZBVubt6vPTzuTIADgXWtpx9aWdH5q0fcQAACbCAIAYPd+lkFLeo41wM7i2Lp72m2DoDlenwGA5solAwBAEAAAggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAAiCAAACIIAIAIAgAgggAASNJqVw/gTRUVFbt6CADQYjWLICil7OohAECL5pIBACAIAABBAABEEAAAEQQAQAQBABBBAABEEAAASf4XeKYNJpiT2FkAAAAASUVORK5CYII=", }, { - name: "Mobile-Multi-Invoice", - template_id: 1003, + name: "Company-Invoice-1", + template_id: 3003, ImageUri: - "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UsK6det49tlneffdd8nIyKCgoIAvv/ySZ555xr9kt8/mzZv58ccf2bFjBy+//DJvvvlmuXUPtNYsX76cF198kWnTpvmX4PbJz89n1apVACxevJhnnnmGzz//nJKSEgAyMjJYsmRJub8ucnJyWLJkyTF/MTgcDkaNGnXMMlVp3+rVqwH46quv+Pe//80XX3yB0+lk7969vPvuu7zwwgvlnhgA71j3xx9/zNNPP80nn3xCXl5epe0QQpx+48aNKzcptyrKbgLpG54C7zyY4cOHV3rdJZdc4n+fn5/PrFmzjnmft956y/+4NxCwKN/xXHbZZQE9Tk888QRut7vK14sgcyaXFT5Rr7zySrlltPfu3asBPX78+ONe71va+rzzztP16tXT0dHRev78+f7zderU0f3799cNGjTQ559/vrbb7bpRo0Y6KSlJt2zZUjdu3FgD+o477vBfc9VVV+no6GgdExOjzz//fB0aGqoNw9BPPfWUv8yUKVM0oNu2baubNWumAX3dddf5z69bt04D+s4779T169fXnTt31oA+//zztdZa79u3TxuGoadNmxbwfaZMmaIjIyN1Xl5ehd83KSlJX3PNNbpLly768ssv9x9/4YUXNOC/rqrtu+iii3Tbtm11fHy8BnSPHj107dq1defOnXVYWJgG9Hvvvee/7ocfftBNmjTRDodDd+vWTYeGhuomTZrotWvXBrRz4sSJAe0RQlSuOlsUhIeHV1jHoUOHdOvWrcttZVD2d+vRnE6nrlu3brlr/va3vx2zvcXFxf7fnZRuYbBv374Ky27ZskWfe+65/rINGjQo93vheFsUjBkzJqB99957r7Ysq9K2PfHEE+XOyxYFwaHGBJlFixZpQL/99tvHvNa3/4YvuLhcLn3dddfp2NhYfeDAAa21N8gMGTJEu1wurbXWCxYs0IAeN26c/5qRI0dqQB88eFBr7Q0yLVq00JmZmVprrbOzs/Vll12mAb1hwwattTeIrFu3Tmuttcfj0ffff78GdGpqqtb6SFB46KGH/P+HmjZtmgb0ihUrtNZaDxkyRPfo0SPgO7Vr105fe+21lX7npKQkfd111+m0tDQdGRmpp0+frrUuH2Sq2r4pU6b4654wYYIG9Jdffqm11jonJ0e3bdtWd+vWTWutdUlJie7UqZO++uqrdW5urv8+SUlJunv37gHtlCAjRNWdiiCjtdYbNmzQoaGhVQ4yWms9duzYckFm1apVx23zU089FXBNhw4d/L8ftda6sLBQf/7557pOnToB5f7973+Xq+t4QWbXrl3+P6x8r2uuuSbgu5WUlOgVK1bobt26aaDcH4llg0yzZs30t99+e8zXd999d9yfgTj1aszQkm/IpF69escsN23aNC666CJ/N6fNZqvwEcJ27dr5n/Lp2LEjcGSM1mazMWzYMAC2bdvmv6Z+/fr+1Szj4uL8E2x9c1MaNmxI586dKSgoYN26df5x3B07dgS08YILLvA/NTBw4EDgyPLco0ePZuXKlWzatAmA9evX8/PPPzNy5Mjj/ozatm3LU089xb333svWrVvLna9q+zp16uR/36FDB//PC7zdy5dccom/vStXrmTjxo2MGzeO6Oho/33+/ve/s3r16oCuaSHEH69Tp0688MIL1brm2muvDfjcsWNHunfvftzrbr/99oCh/tTUVBITE2nUqBHdu3f37+uUlZXlL5OQkMCYMWOq1T7wzsl57LHHAo598MEHtG7dmkaNGpGcnExUVBQ9e/b0D6M//PDDAfcu61gL4vle1Rn+EqdOjQkyvuf4y85yr8jmzZvLzZnxzaavzmN6vs3HXC5XpWV8wcf3H+s9e/Zw+eWXExsby80338zkyZMBOHjwYKV1+BZy8q27cOmll9KwYUOmTZsGwEcffUTdunUZMGBApXXoMk8gjBkzhuTkZK655hp/232h6UTaV5HatWv75/X4FrBKTk5GKeV/+YLXoUOHqlW3EOLUu+222wLmvhxP165dA/6gOXqDyMrExsYyf/78cr+D9+/fzw8//FBucb4LLriAxYsXn/ByDPfffz/ffPNNuT9w9+/fz+rVqwOe2HQ4HNx6660BT8GK4FBjgoyv9+C77747Zrno6Gjy8/MDjvl6c0716o++en2LQV133XX8+uuv/Prrr6xfv/6EHgm32+3cdNNNvPvuuxQXF/Pxxx9z/fXXY5pmlet455132L17N88//zxwJOicivYdzbcwVlpaGto7lBnwSkhIOOl7CCFO3htvvFGtJ4N8G0mGhoYyatSoKl/XqlUrUlNTef7552nZsmWFZRo1asSTTz7JsmXLyu3ZVF0pKSn89NNPlS7616RJEwYNGsTq1auZPHkyISEhJ3U/8ccLygXxKlK7dm2GDBnCO++8w4033hiwa6rL5aK4uJioqCgSExP57rvvsCzL/x/Zr7/+GqDKG6hV1aeffgpAYmIiubm5LF26lBdffPGkNxcbNWoUkydP5u6772bHjh1VGlYqq2HDhsyYMYMhQ4b4j53K9pWVmJgIeJ8SK7tooRDi5Jx77rmVrvcCcN9993HfffdVub7w8HAWLFjA1q1bady48XHL33777XTr1o3IyMhyK/cej8Ph4J577uH//u//+P777wPWjqpVq1ZAb09lpk+fzvTp06t0v8aNG7N06VJyc3PJysoiMzOTiIgIWrVqdczenrILdYo/rxoTZABeeOEFNm3axKWXXsr48eNJSkpi165dvPXWW7Rq1Yr33nuPsWPH0qtXL+6++24effRRtm7dyoMPPkhycnJA+DkRv/76K2vXriU+Pp65c+fyj3/8g6SkJAYOHIhlWURGRrJ06VJGjhxJZmYmDz/8MECVN27zadWqFf369WP69OkkJCTQpUuXarf10ksv5cYbb2TmzJmAd/ntU9W+srp168aoUaOYMGECERERDBs2jN9++42ZM2cyYcKEgE3hhBBnVqNGjaq8D1J0dDT9+vU7qfuZpknv3r1Pqo7qiImJISYmptKeIBGcaszQEkDz5s1ZtWoVF110EY899hgDBw7k/vvvp3Xr1jz99NMA9OzZk7feeotp06ZRt25devbsSa1atQJWpzxRNpuNIUOGEBUVxTXXXEOLFi34+OOPUUphmiZvvPEGCxYsoE6dOlx22WVceeWVDB8+nJdeeqnaaxz4unWPnnRXHS+++CLnnnsuwClvX1nTp0/nH//4Bw888AAxMTH06tWLnTt3kpGRccJ1CiGEEABKH6tv8k/q1VdfZcyYMWzbtq3SvTdKSkpITU0lISEhYHE8n/z8fNLS0oiNjaVFixblNjmrrquvvpq9e/eyaNEifvjhB5o2bUrz5s3L7VlSUFDAzp07/U/7uFwuVq5cSa9evarVhi+++IIrrriC33///aQ2LNu5cydNmzb1z7E5Ve2riMfjYcOGDZX+b/LII4/w+OOPk5eXR2Rk5EndSwghxNmhRg0tlRUSEnLMOS+RkZF07dr1lN83LCyMvn37Vno+IiLCHxLAO3m3KjvP+hQWFrJo0SImTpzIkCFDTnrX1aP3RTnZ9h2LaZqnfB6SEEKIs1tQDy21bt0apRRr16490035w0yePJnLLruM+vXrB+wAHszGjRuHUorHH3/8TDdFCCFEkAnKoaW9e/cGLETXpUuXM/7s/+bNmykpKTmhibfVsWPHDrKzs09Lb9KZsn379oCF8Xr37l2tx8mFEEKcvYIyyAghhBBCQJAPLQkhhBDi7CZBRgghhBBBKyiDzMGDB1myZAnLly8/0005ab4NGk+XzZs3s2TJErZv337a7iGEEEKcKUEZZD799FNuvvlmXn/9dcC7Adill15K//79ueCCC6q9yeEf5ZdffuHyyy+nf//+/t1clyxZwnPPPXfa7jljxgxGjx7NnDlzTts9hBBCiDMlKINMamoqd999t3/jxL59+zJixAgWLVrE999/H7Az9fr16ykqKgK8C7Lt3buX7OxsUlNTAQLeezwe9uzZQ3Z2Nrt37w64p6/nxLdjdFZWFkVFRWzdupX09PQqtfvJJ59kzJgxLFq0yP/o9IYNG+jcubO/TF5eHuvXrw/YBbawsJCtW7eSn5/v35Pk6PYUFRWRmZlJZmYmmzZt8l/77LPPkpSUFLA2jBBCCFFTBG2Q6dixI+Bd/r5t27Zcf/31AWXS0tLo0qUL06ZNo0ePHixevJj58+eTkpLC+PHjGTFiBEOHDmXChAkMGTKEefPm8e233zJgwAAmTpxISkoKd955JwBr167lwgsv5N1336Vdu3Zs376dW265heuuu45XXnmFtm3bsn///oBHwMePH8+TTz4Z0CatNXPmzAkIKWWDzOeff06PHj148803SUpK4sCBAyxZsoRevXrxr3/9i4SEBLZv315he1599VWGDRvGY489xrBhw/jiiy/899i8ebP/5yWEEELUKDoIxcTE6PT0dK211sOHD9effPJJuTLJycl6yZIlWmutn376af33v/9df/LJJ/qOO+7QWms9Y8YMPW7cOK211hMnTtTPPvusnj17th47dqzWWuv09HQdFxenXS6X7tWrl87NzdVaax0fH69LSkr0JZdcojdu3Ki11nrw4MF6+fLlOjExUe/YsUMXFxfr5s2b6+zs7IA2bdu2TXfr1k2PGjVKO51OrbXWrVu31ocOHdL79+/XLVq08F8zdOhQ/eWXX+quXbvq33//XWutdcOGDXVBQUGF7ZkyZYp++eWXtdZaP/XUU3rKlClaa63z8/N1TEzMSf/MhRBCiD+joOuR2bt3LyEhIdSvXx+Affv2ERsbG1Am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evbZZ/XFF1/cls9g2rRp8vX1vS37gl2sevz6pvz582vBggW6fv26HnnkEe3YseOOHKdEiRLav3+/Yzk5OVnnz59X2bJlJUn9+/e/I8cFgKxycnLSsGHDtHr1ag0aNChNzaSnn35aI0eOVI8ePXTx4kX16NFDMTExiouLU5EiRRz7qFWrlq5fv66YmBjHujNnzihXrlwqUaJEhsddsGCBihQponbt2ql69eppthUtWlQVKlRQo0aNdPLkST300EN65pln0rQpW7as9uzZo6pVq97yZ5ArVy7Vrl37lvcDO1l5RuamEydO6PLly3ds/82bN9fevXu1ePFiRUdHa/z48XJycpKPj4+MMVq8eDE3lgHIVsYYOTs7a+HChdq4caN27drluC+mc+fOio+P13/+8x/16tVLBQoUULFixVSiRAkdPnzYsY/w8HCVLFlSpUuXdqxzc3NTamqq496ZP5syZYoCAgL06quvptv21ltv6fvvv9fHH3+sWbNmacGCBUpKSkrXrn79+ipUqNCtfgS4z1kZZGJjY1WlShW1bdtWvXv3VvPmzdO1ee+99xxPH40ZMyZNv5s/N+/ez4ynp6deeOEFvfHGG2rYsKFWrVqlyZMnq3DhwoqMjNQbb7yh5cuX35E5AsA/4ebmpsWLFys5OdmxLk+ePOrZs6e2b9+e5oxIly5d9NFHHyk8PFw7d+7UW2+95Xjq86aHHnpI7u7uGjRokE6cOKG9e/dq+vTpWRpL3759ValSJfn7+ytv3rzavXu38uTJk6bN7bxHBvc36y4tNW/ePMO731955RXHnxcuXJhh36zcNb979+40y6+99poGDBigM2fOqEaNGo4vo5ubm3788UfejArgntG4cWNNmjQpzbo+ffooMDBQTZs2dax788031alTJ1WtWlX58+fX0KFDNW7cuDT98uTJoy+//FK9e/dWpUqVVLRoUQ0cONCxvVWrVo4/v/POOxo7dqxj2d/fX23bttXq1avVrl27DMd66dIlPffcc3r77bfT9AX+KauqX5PcgduLIH7/SklJUWhoqCpXrvyXl3eSk5O1f/9+1apVS/ny5butYwgODlbVqlW5vIRbQpAB7mMEGQC2s/IeGQAAAMmyMzIAAAB/xBkZAABgLeueWkpNTdWmTZskSQ0bNlSBAgXu6PEuXLigAwcOyNXVVY0aNbqjxwIAAP+M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AACAtQgyAADAWgQZAABgLYIMAACwFkEGAABYiyADAACsRZABAADWIsgAAABrEWQAAIC1CDIAAMBaBBkAAGAtggwAALAWQQYAAFiLIAMAAKxFkAEAANYiyAAAAGsRZAAAgLUIMgAAwFoEGQAAYC2CDAAAsBZBBgAAWIsgAwAArEWQAQAA1iLIAAAAaxFkAACAtQgyAADAWgQZAABgLYIMAACwFkEGAABYiyADAACsRZABAADWIsgAAABrEWQAAIC1CDIAAMBaBBkAAGAtggwAALAWQQYAAFiLIAMAAKxFkAEAANYiyAAAAGsRZAAAgLUIMgAAwFoEGQAAYC2CDAAAsBZBBgAAWIsgAwAArEWQAQAA1iLIAAAAaxFkAACAtQgyAADAWgQZAABgLYIMAACwFkEGAABYiyADAACsRZABAADWIsgAAABrEWQAAIC1CDIAAMBaBBkAAGAtggwAALAWQQYAAFiLIAMAAKxFkAEAANYiyAAAAGsRZAAAgLUIMgAAwFoEGQAAYC2CDAAAsBZBBgAAWIsgAwAArEWQAQAA1iLIAAAAaxFkAACAtQgyAADAWgQZAABgLYIMAACwFkEGAABYiyADAACsRZABAADWIsgAAABrEWQAAIC1CDIAAMBaBBkAAGAtggwAALAWQQYAAFiLIAMAAKxFkAEAANYiyAAAAGsRZAAAgLUIMgAAwFoEGQAAYC2CDAAAsBZBBgAAWIsgAwAArEWQAQAA1iLIAAAAaxFkAACAtQgyAADAWgQZAABgLYIMAACwFkEGAABYiyADAACsRZABAADWIsgAAABrEWQAAIC1CDIAAMBaBBkAAGAtggwAALAWQQYAAFiLIAMAAKxFkAEAANYiyAAAAGsRZAAAgLUIMgAAwFoEGQAAYC2CDAAAsBZBBgAAWIsgAwAArEWQAQAA1iLIAAAAaxFkAACAtQgyAADAWgQZAABgLYIMAACwFkEGAABYiyADAACsRZABAADWIsgAAABrEWQAAIC1CDIAAMBaBBkAAGAtggwAALAWQQYAAFiLIAMAAKxFkAEAANYiyAAAAGsRZAAAgLUIMgAAwFoEGQAAYC2CDAAAsBZBBgAAWIsgAwAArEWQAQAA1iLIAAAAaxFkAACAtQgyAADAWgQZAABgLYIMAACwFkEGAABYiyADAACsRZABAADWIsgAAABrEWQAAIC1CDIA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", }, { - name: "Web-Invoice-1", - template_id: 3001, + name: "Company-Invoice-2", + template_id: 3004, ImageUri: - "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mEhxcTE1a9Zk6dKlhIWFcdlll9GuXTurjFKKH374gc2bNxMaGsoll1xC9+7drdfT0tLIy8ujefPmLFmyhN9//51u3bpZ+47s3LmTzMxMLrvssoD3sm/fPvbt20e/fv0q/Po4HA7Gjx/P5s2biY6OLrfMqeqXl5dHUlISPXv2ZPny5Wzfvp1WrVoxZMgQMjIyWLNmDVlZWQwYMIAOHToE3PvYsWMsX76c/fv306xZMwYNGlTuKp9CiL+u6OhoFi5cyGWXXValltyxY8fy4IMPWi0qpbuJKtrpGnwL4D300EPWfk9z5szh6quvrrD8zp07AxbeGzRoEJGRkZWu54gRI/jPf/4DwI4dO/jggw9kQbxgoILQm2++qQC1a9cu69z8+fMVoL7++utTXn/77bcrQF1yySWqXr16KjIyUi1btsx6vU6dOqp///6qQYMGqkePHsput6tGjRqp2NhY1bJlS9W4cWMFqLvuusu6ZvTo0SoyMlJFRUWpHj16qNDQUKXrupo+fbpV5vnnn1eAatOmjWrWrJkC1E033WS9/uqrr6qYmBg1atQo1alTJ9WgQQMFqMmTJyullJo3b54C1G+//RbwfgYPHqz69u1b4fuNjY1V48aNU926dVPXXXddwPMAlZubW6n6JSQkKEANGDBAtWnTRrVt21YBqlevXqp27dqqa9euKiwsTAFq3rx51nU//fSTatKkiXI4HKp79+4qNDRUNWnSRG3atCmgnk899VRAfYQQZ1/r1q0VoAA1bty4Mq9HRUVZr8+YMaPce7z++utWGf/HrbfeetLnjhw5ssw1rVu3PmV9R48ebZUPDQ1V3377bbnlcnNzVf/+/QPuv3HjxoAy+/btC3h91qxZAa+npKQou91uvR4TE6MSExMrrNvatWvVihUrAs699NJLAc9ITU095XsUZ+aCCTLPPPOMAtTvv/9+0msXLFigACu4uN1uddNNN6no6Gh1+PBhpZQvyAwbNky53W6llFIrV64MCBRut1vdeOONClBHjhxRSvn+sbVo0UJlZGQopZTKyspS1157rQLU1q1blVJKHTx4UCUkJCillPJ6verhhx9WgEpKSlJKHQ8Wy5cvt8rccsstqkaNGqq4uFgVFRWpOnXqqEceecR6P4cPH1a6rqu33367wvccGxurbrrpJpWcnKycTqd65513Ap7nDw6nqp8/yDz/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9x5PPvnkGdUVym6Udqb1OxnDMM76OCQhhBDVW1B3LbVq1QpN09i0adP5rsqfZtSoUdx1113ccMMN3Hvvvee7OmfF5MmT0TSN55577nxXRQghRJAJyq6lAwcOBCxE161bt/O+b49/r6Vu3bqd0+f490qq7Nb0wWD37t0B6/9cccUVVleXEEIIcTJBGWSEEEIIISDIu5aEEEIIUb1JkBFCCCFE0ArKIHPkyBHWrl3LunXrzndVzti5fA9paWmsXbu2Wg2GFkIIUb0EZZBZtGgREydOtFbr/eOPP7jmmmvo378/l112WZU3OTxfCgoKzmhBu1NZtWoVDzzwwGnvAiuEEEL81QVlkElKSuLee+/lgw8+oKioiL59+zJ27FhWr17N+vXrA3am3rJlC4WFhYBvsbesrCz27NlDRkYG4NvM0L/CrNfrJS0tjaysLFJTUwOemZ+fT0JCgrVjdGZmJoWFhezcuZNDhw5Vuu4pKSnWTta//PJLwCynE+vrr1NiYiJKKXJycgDfLqwJCQnWXiOmaZKamkpBQQGbN2+26jh+/HhuvfVWaxduIYQQ4kITlAviJSUlce211wK+5e/btGnDzTffHFAmOTmZMWPG0Lt3b9avX8/MmTNZvnw5GzdupHPnzixatIiBAwcSGRnJ3Llz2b9/Pz///DP33XcfV155JatWreKqq65i1qxZbNq0ibvvvpvevXvz5Zdfsnz5ch566CEMw6BJkyb897//JTk5mdatW5ObmwvAP/7xDyIiInj88cetOt16663YbDZ27drF119/zdatW61NFcurb9u2bRkxYgRdunThxx9/5J577mHkyJGMGjWKzp07s27dOiZPnkyrVq24+eab6dOnDzt37iQ2NpaZM2daX6t+/fr9CX8rQgghxHmgglBUVJQ6dOiQUkqpMWPGqE8//bRMmbi4OLV27VqllFL//ve/1UMPPaTuu+8+9eGHHyqllJowYYJavHixUkqpK6+8Um3atEktXLhQ3X///UoppQ4dOqRq1qyp3G636t27t8rJyVFKKdW2bVtVXFysrr76arVt2zallFJDhw5V69atU126dFF79+5VRUVFqnnz5iorK8uqj8vlUlFRUWrv3r3Wudtvv119/vnnFdb3wQcfVHPmzFFKKTVu3Dj19ddfq4kTJ6qNGzda72Ht2rXq+++/V2PHjlVKKbV+/Xo1ePBg6xm9evVSW7ZsOf0vthBCCPEXFnRdSwcOHCAkJIT69esDcPDgQaKjowPKZGdnk52dbW0V4Ha7qVWrFklJSVYLyM6dO+natSsA+/fvp23btiQlJVnnDMOgQYMGrFmzhg4dOhAZGUl6ejq1a9fG4XCwfft2ayn/tLQ0Lr74Yjp06EBycjLz5s1j9OjRAVsk2O12Zs6cyeDBg3nrrbcA2LZtG926dauwvqGhoeTm5rJ37142btxIp06d+Omnn+jZsycACQkJdOvWjcTERKveaWlptGzZEvB1OW3fvr3Su9IKIYQQwSbogkxiYmLAfj2tW7fmyy+/RClFUVERycnJ5Ofnc/ToUZRSZGVl8eGHHzJixAgSExNp164dpmmya9cumjZtSn5+PoZhEBERQVJSkrXJ5Pz58xk+fDh79+4lNDQUgM8++4wOHTrwxx9/0LRpU3Rdx+VykZ6eTv369enYsSNJSUn85z//4b777guod0FBAbfddhuzZs3is88+w+PxkJqaSkxMTIX1ffbZZ/nmm294+OGHWbJkCVlZWdhsvt7AHTt2UFhYiNPpZPv27Va9/e8RsDbVdDgc5/zvRQghhDgfgm6MTFJSUsCmhpMnT2bgwIEsXbqU4uJi5s+fT9u2bRk2bBjt2rXD5XIxY8YMoqKiaNy4Mbquk5ycbP3g37ZtGx07drTuff/99+NwOPB6vSxatIj9+/fz1FNPkZCQAEDjxo3LXOOvz5AhQxg6dChDhgyhcePGAfV+5ZVXWLFiBRkZGTz99NMYhkFcXByLFi1i1KhRZerbtm1bANq0acOyZcuYNWsW//rXv3C73XTv3p2oqCi8Xi9KKbZv386jjz5q1cc/JiYxMdGqpxBCCHEhCrotCiZOnMgVV1zB+PHjrXNut5tt27bRrl07wsPDT+u+x44do3fv3nz//ffs37+fjh07omlale6Rnp7OqFGjeO+992jVqtVp1aO0ffv20axZMzIyMhgwYABbt26t0vXPPvssDocjYMCxEEIIcSEJuhaZHTt2sG3bNqKiohgxYgTgG39ypps1+ltZoqKirNaaqvjwww+54447eO21185KiElNTaV79+60aNGCkJAQZs2aVaXrhw0bxtatW6t8nRBCCBFMgq5FRgghhBDCL+gG+wohhBBC+EmQEUIIIUTQkiAjhBBCiKAVdIN9hRBCVN0Dfb3nuwp/KS9/a5zvKoizRFpkhBBCCBG0pEVGCCFEBf7MSa1VW7dLCD8JMkIIIQIoFMr0YuLhrISZCm+hAZrvf5oOmo6m+Y6FqCwJMkIIISxKKRReMIoJqZWL4fCe0FhSNpWoMqdVwKG3GJQXvEUaKF9RhYamNFAaGqEoUwevHWXa0TXDF2yEqAQJMkIIIQB/iFGYyo3DmU3v0TnUbmTgTx+maVLeEqpKmSilrDBjer14zZLX8IUYt1thegA0XB4Hphs8+SbKq3C54HBaCEd+d+I6Fo5phqIpO5qmS+uMOCUJMkIIISxKefGqYrzkEhHpJrKmr/3E9xqYpsL0KpQCTQNdB1BopolumsfLajpe3cBUCm9JgCl5AppWjDIVuteD1+3B7VXEtNfIOJDPb+ucZKXWAHckhuYAzajyvneiepEgI4QQwqJQuL35eIqyMM0w35gVTcM0weNSpOzI52hGER43GHaNBs1DaNxAw75nJ460NFAKpet4LmqGu3krDh1wcXB3MUqBbmhE1rXRol0YNq+L0K1bMI5mkef24NYMGl/cnrABjdixsZCMPSaeAicGYejKJmFGVEiCjBBCiONK+o5OGPKC16NI2+1i4b/SyTxQhK5r1KxvY8T9DQjJ3UX4S/9CO3AANA1Vty6eu+4mr1YrVrx3lOQNeQCER9oYcHNtWrTy4EjcTtgbr6MfOkQIEN20KTn/cy8pKhQ9wqDtlTb2/OimOAuUGQZImBHlk9FUQgghTkopKMhTfLcwgwO7CyjI9VJcaNK0XTgtWypClv4f2q5daMeOQWEh3ktiKWrfla0/FJG8IY+CY16K8k3qNnXQtns4ofnZhC/4GP3339Fyc0EpPL37kNOwOQnf5fPLDynUb1pEz2F5hEQdxaQIpbzIHseiPBJkhBBClEvT8HUreSElqYCkH47h9SgMQ6NhyxAuH1aDGnt+xbZhPZrHA4aBatqUoqHXcCDTwQ+LMijM86JpUKOmjZ5DI6lZy4v+8yaMhAQ0jwdlGJht21J4ZX/2pxaza1M+6YeOkn4gl3aXOuh81THsNbIxKUap42NwhPCTICOEEKIMDdA0HaUg/5iXTcuzyT/qQQNCnQaXDojkojr52D77FDIyQNMwIyJwXz2YwsYt2PpdLpmpxaB8Y2k6XRlF224RhB5JJ2zp/0F+PgDK6aRo0GCyQhvw0xIPxw4ZqCIbWRnZhITZ6Xi5gzaX56GF5mIqF6Yyz+vXRfz1SJARQghRLk3X8HogaUM+OzYcQ5m+AbsXtQ+l+4BInElbsK1fj+b1gmFgduhA8dVDSEkx2LwyF3eRiW5o1Kzn4LJhNagR7sax8muMbdvQlELZbHi7XkLRpXHs3l7M7k2FmG6F0rx4PEXYDIMa0Xa69rNTr20eHi0XpTzSxSQCSJARQghRPgWZaR7Wf5FFcZ4Xm0Mjqo6dXkNqUav4D/Tly8DjQdntmDVr4r56MLmhtdi4JJNjmW4Mu0Z4lEGv62rRqEUYIfv3YV+3DrxelN2OatqUwtHXc9gVxFUUQAAAIABJREFUzYYvc3AVmRiaHYdy4ozWMWwGNpuNyNpw6VUeIhvk4SYPU8KMKEVmLQkhhKhQbrabuo0N6jSMAk2jfvMwOveNxLFrD6pOHYr790ez21ENGuC5/HIK8g3CIhx07heFpimi69npMaQGIWEaKusoxc1bQOMmYBjQqSPuzp3J3+2lXlM70XVr4PV6MCMKqdfEgc1uoGkQohQxraBL/2J+WlyANz8UpXRAl5lMQoKMEEKICmjQsnMELTuHYpoKXdfQDQ1D11CXdMXVqRNeTSsZUKODrlO3Jgy/p2HJgnkeNE3DZvcNHHbFxWFe2t13bwW63UDTNZq1h5jWIZhK4Spyc+iQlwingxCHA4/uAcAMM2nfw0tuVg6/fmvHW6CjK0fJVgYSZqozCTJCCCEqFBKigan7unJ0rWSdGd/4Fs1mw/B6UZqOpuuYKHRdEWpTvvKaHjDJyBbigJCSrRBMBZrvc7sDDJtWUtbEbncQFh6KzeFAobArhWmahDlNLrnKJOtQLqlbdJRZA5QDXTPO01dH/BVIkBFCCFEuQ9cxCgqx/bgRZTOgdRu05F9RR7OhXl1fgNm/H9WyJe7YS0D3BR6llLVHktJUmRnTmqb5zkNJi4pC133bHyilME0vGhq6rqPrBuABDd/g3ygbXa8yydiXT9FRHV0ZKOliqtYkyAghhCiXpunYcrN9s5JcLjh8CD1pB6bHg5adjTp2DL1+fTzhYaiSrQyUUui67mtxAd9+k5ov2JQeoFs6ePi6oTRAUVhYRHrGQRR12btrDy63C4/Hg67r2AwHugF1G+v0uMZk01IozLSXhCZZ+be6kiAjhBCifErhjQjHCAlBud2+8NKsGabHA3XqoOfloerUwVu3nm8QDPhCjFJoekmLjHm8dUZDQ5XspO0PHf5w49/TKSTERoOGTXAVFbFmzRoKi4pQysTr8WLY7DgcNhrWb0Stho1pe3kU27524C3UMQhD0+RHWnUUdH/rpmny3XffAdC9e3ciIiLOc43Kl5ycTL169ahVq5Z1rqioiB07dhAbG3vG93e73fzwww8AxMXFERoaesb3FEKIE6nIKNx9+vg2g/R14uA1zeObSSqF8q2eZ11TOqRYrSTKtyGlhmaNjfGX1UqFIEdICDVr1qSooAg0hdvlwmua5Obm4Pa4cbvdZGZmUFRYRGSjRsR0gf2bNbwuDVSYjJephoJuHRmXy8XgwYOZNm0a6enpvPzyywwYMIDhw4ezZs0aAFatWkV2dvYZPyspKYnExMQKX3/ggQcqfM6///1vfv7554Bzhw4d4v777z/jegEUFBQwbdo0hg8fzqFDh87KPYUQIoAG6Dq6zYZut2PYHWh2O4bDgWazgWGgGQaaXv4YFU3zjXPxhxVN94UYzZrpFFhe133lQ0PtRNWMpGGjRjRo2IRGjWJo0eJiWrVszcUtWtOoYQxh4U4coSatexTSsF0W2PNlfZlqKuiCDECdOnVYuXIlUVFRvPnmm6xcuZLFixdz1VVXAfDcc8+VCRiZmZmkpqZax9u3byc3NzegTEpKCgcPHrSO58+fzy+//FJuHfLz85k/fz4LFiwIOH/06FFSUlICzimlSExMxOv1Bpw3TZPt27db5098Pvhadkr/w/QfR0VFsXLlSlq2bFlu/YQQ4lwpE1DQ0LXAMKMR2EKj6cevQcP6XNcDfwx5PSZFRYWEhTuoERlBVM0aRNeKpG79etRtUJ969RtSt14DatWqTVR0NLUbanQe4Ca8TmHJqr+yhUF1E3RdS6U5nU4Mw+Cbb76xQsyWLVtIS0vj9ddfZ+jQobjdbp599llatmxJr169mDhxIkOGDCEmJoaEhASeeuopRo0axSOPPML27dsB6Ny5M48//jgbNmxg586dmKbJhAkTAp49f/587r//fubNm8ff//53ANavX88999xDXFwcq1ev5vrrrwfgxhtvxDAM8kv2FgHo06cP9erVo2bNmrzwwgu8+OKLAc+fNm0a8fHxtGnTBo/Hw9tvvx1w/O67757zr68QojrTSnX7nLyVQ9d1THW8u8n65aucy8p0OynfVgj+sl6PSXFxMYbhCzo2m8IsGTisFNjtvtePHMkhqqYTm82GM8qG0nPxKCe6FnK2vgAiSAR1kHE4HCxatIhJkybx2muv8c4779C1a1eaNGnCPffcQ7NmzVixYgURERF88MEHALz88stcffXVPPzww+Tm5hIbG0vz5s1JTk5m2bJlAPTr14/MzEx69epF69atufXWW8s8e/bs2Xz99desXbuW5ORk2rZty2OPPca3336L0+nk9ttvB+Czzz6jU6dOPPHEE+zbt49bbrnFusekSZO48sor2bx5c5nnb926ld27dzNr1izatWtHVlZWwLEQQpxruqaj+7bAtqZVlxYwvqVkzRiFChwbA9Z5/zXl8d87JNROpBZ5vLVH09D1kuVrUCiloUwbhs3AMHRsNgObzQuaae2OXeb54oIW1EEGoGPHjqxevZpXXnmFe++9l48++qhMmYsuusj6fNu2bXi9XqZOnQrAhAkT2LJlC8eOHbPO9e/fn5CQilP9jz/+iGEYbN26lTZt2jB37lzuu+8+jhw5gtPpDCj7zTff0KtXr3Lv07x5c4Byn1+vXj3ef/99br/9dkaOHMnkyZPLHAshxDmlgab71nnxKx0SAqZT+wfxmsenX1vdS6UzjQoMNNYsppJZTjabDjjKBBHfoVayUrCN+g0a4HA4KMlZWHdTpiz0W80EfZDJy8vD6XTSu3dva7CvzWajoKCg3PKdO3cmLy+PKVOmWOd++eUX7HZ7wDn/fQoLC8vc45133qFDhw58//331KlTh/fff59nnnmGw4cPk5mZSZ06dayyzZo146effuLGG2+s8D106dKl3Ofb7XZWrlxJixYtuOWWW+jYsWPAcd26dU/9BRJCiDPkDxXaCS0zAd1IJ5YtCSamMq3p1v4QVNE1/utOHDzsDzulTuB2uQkLC/WNtdEVIGNjqqugDjJer5c+ffpQu3ZtcnJyePPNNwEYOHAgI0eOpF+/fowYMSLgmrvuuouhQ4eydu1aXC4Xjz/+OEOGDKFTp0707NmTyMhILrvsMp599ln69u3LbbfdxqJFi/j8889xOp0cO3aML7/8kpSUFMLCwgDf2JhVq1bxzDPP0LZtWzp37szhw4e5/vrrGT9+PH379rW6vMrTrVu3Ms9/8MEHad++PZ06daJjx44YhkGbNm2s49LTuoUQ4twIHLDrDyCGYWCavuBwYjgp05KiSlpqSqZeK05e3u32UlxUhCPEQUmfFKZSgY0sGoSFhVAyxMZqjSHgT1FdaCrI5qoVFRXRqlUrawaS2+0mMTGRDh064HA4znPt/nyxsbF89tlnNGvW7HxXRQjxF/ZAX+8pyyil8JouiryZ6BH7ufnx2jRuEUZ5w03MkrVk/IHGf3159zzxc+tPThh3oyA3p4CMjAzq1a+Hy+UCFMdycnHWiMDhCEVDcSw3l+KiYho2boDdbpB3zMNnrxeQta8R4bZ6GHrIKcfIvPytrDdzoQjK6deZmZnEx8fz+++/Y7fbueSSS6pdiMnJySE+Pp49e/ac76oIIS5EJeu9+Na6KxsKSk+bLj0wt+zYlvLP+VtodO34fRRQlO/ARn3S9oWQmxdObm4ExYW1OHo4jKPpNnKO2CkqikIz6pOba0OVjM2x212YqtAaVCyqj6DrWgoNDS133Ep1419HRgghzgVd131ToEsG+1or+ZZqgTlxHRj/6xWNhyk9PVsv+T3aWu1Xgcet8XuyjleFcDhV4XKF4MkHW5gNswh0TSMkVOHx6tRs6KVujEHNWl7fYF+dUiHmeIeTuPAFXZARQghx7mklLTKlp1/7zpe/+aP/nLXXUgXlAp+hHd93SQPDAGeUorDQxBltwxGmwAP2cIVdU+RkGqDAHqIRWd9GvUa+FiPD8AUZXz1O3YUmLiwSZIQQQlSsZEG8gJlF5awrU1FQObGcP+SUNxPK7tBoH+vA6/W1qBzfNkkD0wRNxzcJSgdMNN083v0FaOWsdSMufBJkhBBCVOB42IATNoGkbFdS6fOllTezqaIp2LoNdMO/S7a/s0iVzHwyOT6kxhewlNKsDSslwlRPEmSEEEKUS9cDW01O7Ebyj5EpHVQqCimlW2D8ypuCbZR8bppmqS4tDaUp//K+pe9cMgXbhkJmIVVXEmSEEEKUUJRu19AqGDBb3kykyoaYEwcLV9QV5B9ErJSyrvGvGHzizCTDUOi6kuG91ZQEGSGEECXKG+fibzEptfRcBWGldJipzCq+JzqxrFK+0OJv9QHfyr/+1YL9gcbQtXLXuhHVgwQZIYQQ5dN8U5594UPH6zV9XTkK68+A9ezU8Qv9wcIfXLzeE7cQON5t5G8F0tCsVXxNVWqhvRNaYDR00FRAeVF9SZARQghRLg0Nr6nIO1aIx6tZZyscVltyuvTKvSe+Vt5FXq8Xr8ddsnu1Vonrjp/UDXDYDAkz1ZgEGSGEECVOSA0amKbGsVw7XlWDyi0yV8W5QwpM00tOdiYej/v4gyspNMRNvbpG6S2wRTUTdEHGNE2+++47ALp3705ERMR5rtFfy65duzhw4AANGjSgbdu257s6QoigUjYJmKaJUgZgL/f1QKcxAVoDpUy8JnjNqu+ao5Skl+ou6PZacrlcDB48mGnTppGenk5xcTH3338/vXr1ol+/frz77rsAfPLJJzz//PMArFq1iuzs7Co9p7CwkDvvvJNBgwYxZMgQsrOzyczMZPXq1Se9rjJlKisiIiJg/5KdO3eyb98+hg8fXuE1y5cv54EHHuDf//73WamDEKK6KlnXxdqCoDKBQTu9D03nTH4c+b5HnvblIsgFXYsMQJ06dax9hp544gnCwsLYsGFDQJkxY8ZYnz/33HPMnTuX6OjoSj9j+fLluN1uVqxYYZ1bsWIFCxcupH///ta51NRUNE2jSZMmAPzyyy9lyvh36O7YsSN2u73SdcjPzwdg2bJlzJ8/n9atWwOwePFiq4zL5SI5OZmOHTui6zr33HMPjRo1YtmyZZV+jhBCVMQ3WPdcpgSF2+XGNE9/a4FT7XQtLmxB1yJzov/93//liSeeCDh36NAhOnTowAsvvMCWLVtIS0vj9ddfZ82aNQwaNIjExEQAPv74YyZPnlzufVu0aEFCQgJ79+61zi1btoxt27YxdepUTNPks88+4+6772bKlClMmDCh3DI7duzg0ksv5a233uLSSy/l119/rfJ7fOqpp6z3OHLkSAYPHgxA+/btueGGG3jyySe54ooryM3NrfK9hRCiIrphoGkQEgLOCI2IcC3gT2e47/MaJ5xzlioX8NqJ58I1IsJ1atQIISzUICSEKn/YbL7hMYY96H+cidMUlC0yfunp6TgcDmrUqBFwvkGDBkyaNImcnBy6du1KkyZNuOeee2jWrBnp6em88847vPrqq3z88cc888wz5d67S5cuTJs2jfj4eMaMGcNzzz3H4MGDycvLY8qUKYAvVAwbNozNmzdzzTXX4Ha7y5T5xz/+wezZs+nZsycrV65k+vTpzJkzp9Lv8dNPP6Vt27a0a9cO8LUuPfzww9brc+fOxel08vjjjzNnzhz+3//7f1X5EgohRCknTnMGXYeICHA4dM5Ny4zC5TJwu3Q8Hq1qw2w00PGtLuwI06VlppoK6ghbq1Yt0tLSyMnJqfQ1o0aN4ssvv+TQoUPs37+frl27Vlh26NChJCQkkJKSwjvvvFPm9bfffpuBAweyePFiQkNDOXLkSJkyW7duZcmSJUydOpWNGzcSHx9f6bqapsmUKVPKtDiVJyYmhgMHDlT63kIIUVb5QUDXfIHm3HxoaJqJhln151hr1ciEpeosqFtkbDYbI0eO5Pnnn2fGjBl4PB7Wr19Pnz59ypQrKCgAwG63M3r0aMaMGcP48eNRSrFlyxYuueSSgGtM06S4uJjIyEi6du1KTk4ONpuNwsJCq8z//u//Mn/+fFq1asVXX32FUqpMmS5dutC/f3/69esXcP8tW7bQqVMnDKPi/UHmzp3LpZdearXGnMyGDRsYNGjQKcsJIURl/ZmDaE+7NUXDv/zwWa2PCB5B3SIDMGPGDBYvXkzr1q2JjY1l06ZNZcoMHDiQkSNHctdddwEwduxYfvjhB8aNG8exY8cYPnx4wFgYgKSkJHr27El8fDwrV67kzjvvpEuXLvzwww/Ex8ezbt06rrvuOm6++WbGjBlDjRo12LBhQ5kyU6dO5YEHHiA+Pp64uDgOHz4MwNNPP33SLibTNPnnP//J9u3biY+PJz4+nqVLl5YpN2LECHr37k2jRo3429/+diZfSiGEOK5ULlAcX8X37H8oUDpKaad1vW9jbAkx1VlQt8gAXHzxxSQlJbFt2zbatWtHaGgoAHfffbdV5rHHHuOxxx6zjkNDQxk9ejR169YFfF1IMTExAfft1KkT69atIyUlhY4dO1q/Lezbt88q07t3b5588skydSpdBiAhIaFMmT59+tCrV68K35eu6+zevbvc10rPSPr8889xOp0V3kcIIU6HrmvoNt+4mIJ8k4ICL+eqA8flNsk5pvB4TtyM4OQ0IMTu26pAxsdUX0EZZDIzM4mPj2f27Nk0b94cwzDKdA1V5KuvvmLGjBm8//77ACQmJjJ58uRyp0VHRkbSqVOns1p38E2Z7tChQ6W6jKrq9ddf57333qNbt25n/d5CiAtdqRihab4NGk2NomLfnkbnaKwvXlPh9oDHXcWxvhrYDA1ND/rOBXEGNFXRHupCCCEuGA/0PfU6LUopvGYxRd4jhNb+g7v+GUN0XYPUNBOTSM5Vi4zXU8DRo0fweKq+lkx4qJtmFzlZ8n4GO76rQajeGIctAk07ebh5+duKxyeK4CIxVgghRFkaaLqOrhvnfhytZudMfhzpuoYuXUvVVlB2LQkhhDgXSsao+Kc16xqaptB1hTLP3TNNXMAZruwrOabakiAjhBCiRNn5P7oOTifo+rnriil2heFy2fC63VR140mHQ2ZeV3cSZIQQQpQoJ0RovlV9bbZzt+eSpmmEhWh4bVXbzVrTwDD8rTGSZqorCTJCCCFKlA0DvkXxzu3CeLruQdNN8J5G64oCXdPRdQky1ZUM9hVCCHEeKVA2qEJLTGmmCWgaNptNGmWqKWmREUIIUaKcBekUmF4PXorxTWXynSt/JMvprebh9nhQmGiaWfV7KFWyS4GkmOpKgowQQogSJw721bDZbYSGFWOax6yzvm0FSiKHUiiUb7uAihMOgS9oAefsNkWtaA1T2Y7fHw3/Mmdaybnybq1pNmw2I+CuonqRICOEEKJCNrudqJpRvoOSvZGU6dsWwAoymkKZpi9IKOv/OB5DfH8qpVDK9H1OyXxupQKvUWAqE003ArKPaR5vrfGtGaNQSmF6TczT7JYSF4YLIsgcPnyYvLw8WrZseb6rIoQQFwxd19ANHWWaoPnCRFG+SdKmLHIz3L5WGM2kVddIGjU0cP+yCQ4eBEAZNjwdOmBr2pw9SblkpHowTQWaot5F4VzcKRyOHoHNmyE3z/dAQ0eL7YandgN2bs0h65DLVw8DmrYJJ6ZlGCo9HfXLL6i8XJSpwOlEj7tMWmOqsaALMitWrODaa68lPj6eiIgIJk2aRGpqKtu2bWPGjBnnu3pCCHHBMDTfqr66zfC1iCiNXxOyef/pJI4eKkbToHbjECa92BH3kT0UPTcVlZoKgNmiBfbp0zmQks/7z/3KH3uLQEFkbTs3PnYxRucIXN+sxTVzJlpeHugaWoeOONq2J/n3o8ybvoeMtGIA6jYNZcIzraHYwLVoEWrePFReHhgG2lVXYevRC9+OBBJnqqOgnLU0cuRIlixZwtNPP81NN91knU9NTSUrKyugbHJyMunp6QHnPB4P27dvDzjndrtJSEjA7XZb5zIyMsjLyzsH70AIIf6KSo9D8e8o7QsHylQUF5ms/GgfKTtyOXq4mIJjHroPqEWT+iZF776LuX07Kj0d8+hR7D16EBLTjE0r09m5KZejh4o5dsRF03Y16NizFuQcxf3FF5CS4rsmNw/9qqsoqFGfbz79g9+355J9uJi8oy5aXVKDmJbhuFN+x7tgASotDY4cAcDWvz+a04luNyTHVFNB1yJTmmEYHDlyBKUUS5cuJTs7m2XLlvH2228zaNAgxo4di2EY7N+/n9GjRzN58mR69OhB+/btycvLIysrizVr1rBjxw7GjRtHz549+fHHH/n4449p164dd955J82bN2fmzJnn+60KIcSfoOzKvgAo8LgVv6w+yJZVGXhcCl2Hi9rXoN/wprBuBebatVBc7FsIpnMnbNeNYM9eF99/cQh3sRcU1G0STv8xTYlwmBR+8hXm5s1oHg8YBkZcHI7+A/jxp3wS1mbhdvnG0LToEkm/0Y0I9RTg+XgBpKaCx4NyODAGD8aI64my2yTDVGNBGWR+/fVX7r33XhYsWMBDDz2EpmnEx8czc+ZMFi9ezKeffopSivDwcObOnUtRURG1a9fmf/7nf8jLy+Oll16idu3a9OjRg4MHD/KPf/yD2bNn07NnT1auXMn06dOZM2cOr732GmFhYef77QohxHllmoqMA0V88Z995GS4QCmc0Q6uGnsRDaJc5C9ciMrKQlMKFRVFyA034KrfjK+mpZCaXIAywR6qEze0Pm0uiUSl7MT73/fRsrN9D2jQANvf/saxkDp8v3gneVluUBAeaWPAjU2IaRaG+ct6zBUr0Ip93U3aRRdhv244enQ0GHY0WRCv2grKIFO3bl1GjhzJbbfdRteuXfnwww+x2+0A1K9fn9zcXA4ePEjDhg0BCA0NpW3bthw6dAiAkJAQq2x6ejpbt25lyZIlfP311wDEx8cDEBMT82e/NSGEOI/KnzvtcSvWL8vgt03H8HoVNrtOl351uGJgLYqXL8T788++lhWbDT0ujpBrrmH7lny2rjmEq9CLpkGz9hH0HdWICIebgiVLULt3+65xOLD174+9V282LM0gaf0RPG4T3abRtmc0sf3q4ijIxvXRR77WGNNERUZiv2EseseOoOtomo70K1VfQRlk6tSpQ79+/U5aplWrVnzxxRcAZGZmkpGRQZMmTcot26VLF/r371/mnhkZGYSFheF0Os9GtYUQ4i/ueBjQtJLtAkw4+Hsh679KxVPsQdc16jcNZ9CNzXEeSyVv/ny0/HwwDIiJIfTmm8lVTr77/Ffys93ohkao02Dgzc246OIIin9ej/nFF74Qo+vQvDn2kaNIy9RYu+AQrgITTdeoFxPK4AlNiY428C5f7wtLSoFhoPfqRci1w1GhoSUVPX9fMXH+BWWQqYy+ffvy6quv0q1bNwoLC3n88cdxOBzllp06dSo33XQTderUITc3l8WLF1O/fn0ZIyOEqGZ8g319K+X6znhNjd9/y6Fu0xDi6tRH0zTadqtJxx7RuNb9DA0bojVo4NsmoFccIVdeScrOYoxQG5cOrlcyNsZJryGNsOsm7tRU6NgRrXVrlGFg9OuL3rEj+3/MJ7pRGLH1QrHZNC7uFkmnXrUwlAtPWhp6XBy4XGDYsN8wBlWvrm9etq/aohrTlH/pRCGEEBesB/p6T1lGKYXXdFFkZhJa+xB/f7YFdRvZycv14CryzejUgNBwG2HhOiovzzcNWvc132gREejOGhQWeijI9fjKa2CzGzij7GjKizc7G1VY4HuepqE5nRAaRn6Bl+J8Xx11Q8MWohEapqGZJnpeLprbdz8FmM4IVEiI7+YKUDor5mfw4xKDEBrhsEWUdDdV7OVvjdP7Qoq/nAu2RUYIIcRp0HQMQinIdvDhzF/xOA7jUcXWtgS+Iv51fRWY/pOUdPPox1cALmne0dBK1nlRmB4PyvSHKs0Xgvx3M0tN/rZ23FYlq/+W+p3b3++laWjohFKLvPRaoOpKN1M1JEFGCCGERUdD1+yYrnB+T8yl0KXwlmnc8IeK4+vMlH2tonM2lDp7P3o0NMI0GyGhoUTYnRi6o5w6iQuZBBkhhBCAfwdpHUNz4NBrgkPHpkcBir/sdkYKDN1OiC0KmxaOrsuPtepG/saFEEKU4muRcRgRGJqNUKMWvkHAJV1JJWXKUiec928sWfZ8xddX5d6lmok0DUN3oGsGGnpJIBPVhQQZIYQQFt/YFANNaeiaDVCYqmTnad+m0xVcyPG8UV6ZqpyvVFnNmlmloYOmlwzTkRBT3UiQEUIIUYam6dZgW02VmuFzspygnaJMVc5XoayEl+pNpl8LIYQQImgF5e7XQgghhBAgQUYIIYQQQUyCjBBCCCGClgQZIYQQQgQtCTKAaZps3LjxfFdDCCGEEFUUdEFmxYoVJesc+D5at2592vf65JNPeP7553G5XNxyyy1nsZZCCCGE+DMEXZABuOGGG0o2JFPs3LnTOn/06FHr+PDhwxw+fNh6ze12k5CQgMvlss6NGTOGJ554osz9U1JSME3zHL4DIYQQQpwNQRlkMjMzWbt2Ld999x0AaWlpxMbGMmXKFCZOnMjQoUN56qmnGDVqFHPmzME0TUaMGMGcOXO44oor+OWXXzh06BAdOnTghRdeKHP/Ll26sHjx4j/7bQkhhBCiioIyyCQlJTFt2jRmzJhhnTMMgzfeeIO33nqLwsJC/vOf//D000+zdOlSdF1nyZIlPPLII/Tu3ZvFixfToEEDJk2aVO79v/vuO4YNG/ZnvR0hhBBCnKag3KKgb9++fPTRRwHn7HY7ADabDcPwLadds2ZNXC4XmZmZ3HjjjTRp0gS3201UVNRJ79+5c+dzU3EhhBBCnFVB2SJTVevWraNhw4a89957DB06lBN3ZbDZbHg8Huu8jJERQgghgkNQBplvv/2W+Ph44uPjueOOO05Z/oorrmDNmjXccccdfPbZZ/z8888Br9tsNoYPH84jjzwCyBgZIYQQIljIppHgwb1xAAAgAElEQVRCCCGECFpB2SIjhBBCCAESZIQQQggRxCTICCGEECJoSZARQgghRNCSICOEEEKIoCVBRgghhBBBS4KMEEIIIYKWBBkhhBBCBC0JMkIIIYQIWhJkhBBCCBG0JMgIIYQQImhJkBFCCCFE0JIgI4QQQoigJUFGCCGEEEFLgowQQgghgpYEGSGEEEIELQkyQgghhAhaEmSEEEIIEbRs57sClbV3797zXQUhxBlq0aLF+a6CEOICIy0yQgghhAhaEmSEEEIIEbSCpmvJLycnh+TkZAA6dOiA0+kEoKioiIMHD9KiRQtSU1PJzc2lffv21nVKKX766f+zd+dhVVX9+/jv4wHFEUFUVFAGFQEFTExJRNJQccghMs0ppZBm0zIrHMmyckqyAR9z+DwhWg484ggimYoTooCIA4ICijLIPHPW9w9/7F9HQE1FWnK/rovrcq+z9t5rnQ6nmz29T8La2hotWrSost0rV64gMzOzSnu3bt2gr6+PtLQ0ZGRkwMrKCrq6urU0OyKi2nHnzh2cO3cODRo0QJcuXdCuXbsqfc6fPw9bW1sAwLFjx1BaWlqlj52dHaKjo7XaevTogVatWtXOwIkeQCWEEHU9iIdReY3MsWPHMHnyZLzwwguIi4uDq6srli9fjhMnTuD1119HQkICVq1ahTNnzmDTpk3K+hUVFejatSsCAgLQp0+fKtufPXs2du7cWaV9/fr1CAoKQmhoKAwMDFBQUICVK1fC2dm59iZL9IziNTJ15+DBg3jppZeU5XHjxmHdunXKH4MajQYmJibYunUrnJ2dYWpqipSUlCrb2b9/P4YMGaLVFhwcjOHDh9fuBIhqIO2ppf/7v//DDz/8gJ07dyInJ+ext7d8+XIkJCTgu+++g6mpKRISEpCQkIAbN27g9OnTCA0NRUhICEaPHo05c+agrKwMOTk52Lx5MwoLC5/AjIiIal9JSQl2796Nc+fO4ZNPPlHag4ODcfPmTaxfvx4AkJycDCEEPD09MXXqVAghIISAWq0GAGVZCIHhw4fjzp07+OWXX5Cfn18n86L6S9og88MPPyAwMBB9+vSBvr5+re3nwIEDGDFiBFq3bg1dXV14enri1q1bOH36NI4cOQIfHx+cPn261vZPRPQkNWzYEMOGDcNrr72GXbt2Ke0bNmzA5MmTERgY+FB/HIaHhyM8PBy3bt0CcPe70tvbG0eOHKm1sRNVR7prZCppNBpoNBoAQHZ2dq3tJysrC82bN1eWjY2N0bx5c9y5cwfu7u7YsmULHB0da23/RES1wc7ODunp6QCAmzdvYteuXbhw4QIiIyMREBCAt99++77rL1myBADw6aefom3btnj11VfRoUMHnnanp07aIPPBBx8AuHued/fu3ejcuXOt7KdDhw5ISkpSltPT05GXlwczMzM0aNCAIYaIpBQVFQVLS0sAd4/G6OvrY/PmzTA0NIS/v/8Dg0xISIjWcoMGDRhiqE5Ie2oJANLS0nDlypVavUZl4MCB2LdvH44dO4abN2/i888/R69evWBjY8NrZIhIOmlpadi9ezf8/f3x2muvQQiBdevWKRcCDxo0CGfPnsWJEyf+0XZ5jQzVFWmPyFT+JTF8+HC89tpruHDhgtbrR48eVfro6OggLi4OAPD6668rfT7++OMH/tUxfPhw7N69G5MnTwYAuLm5wc/PDwBw6tQp+Pj4wMLCoto7oYiI/m0sLCxgb2+Pd999F5988gkOHjyIxMREHDlyBMbGxgCAw4cP49dff73v95pKpVL+fejQIeTk5MDb2xtWVlZwdXWt7WkQKaS7/bquJCcnQ61Wo3379kqbRqPBmTNneHqJ6CHx9utnl0ajwbFjx3h6iZ46BhkiemoYZIjoSZP6GhkiIiKq36Q5IkNERER0Lx6RISIiImlJd9eSRqPB4cOHAQC9e/dG06ZNa3V/6enpOH/+PPT09NC3b99a3RcRERH9M9IdkSktLYW7uzuWLFmC27dvY8KECVpP9rWwsEB5efkT219cXBwWLVqEKVOmPLFtEhE9bUVFRbh48SKAu898SU1NVV7LysrCkSNHkJqaij///FNrvZiYGMTHxyvLKSkpiIyM1KqMnZSUhDNnzmitl5SUhOjoaFRevZCYmKiUNaj8qaioqDLO4uJinD59GhUVFYiIiHj8if9DN2/efOr7pMckJFNUVCRMTEyU5f79+4tRo0Ypy+bm5qKsrEwIIcT169dFcnJylW1cvXpVZGZmCiGEOH/+vCgqKtJ6PTo6WuTm5irL6enpokuXLk90HkRET1N4eLio/MoPDQ0VNjY2oqCgQAghxL59+4SRkZE4c+aMACBiY2OFEEKUl5eLNm3aiLVr14qSkhIxadIk0aJFC2Fubi5at24tDhw4IIQQYvHixcLFxUUIIURGRoYYPHiwaNGihbC2thZWVlaivLxczJ8/XwDQ+snLy6syzt27d4tJkyaJ48ePi6FDhz6Nt0YRGBgopkyZ8lT3SY9PuiMy91KpVDA0NMTq1au12rdv34533nkH8+bNw7Rp0wAAv/32G9zd3bFy5Uq88MILGDduHH766Sf07dsXMTExKCkpwaBBg7B8+XI4Oztj27ZtdTElIqJaFxcXBy8vL622nj17ws7ODtu3bwcAhIaGorCwEBMmTMCGDRtw5MgRXLx4EfHx8Zg0aRKmTZumdWQGAHx8fJCbm4ukpCTExcUhJCREqZj90ksvaVXNbtasmda68fHx2Lp1K1q1aoWAgAAYGxtXKWAZEhKiXF7wJC1YsADLli1TaviRPKQPMgDwxRdfYOvWrTh79qzSNnbsWGzfvh3e3t4IDg5GWVkZAMDa2hqrV6/GBx98AGtra/j5+WHkyJH4888/8eOPP2Lo0KHKL+zcuXPrakpERLVKT08P+/fvx3/+8x+t9qlTpypBZsuWLRg3bhyaNm2KnTt3Yvz48TA2NkbDhg0xa9YspKamVql2/dtvv+GTTz6BgYEBAMDU1FR5LScnRzmtVFJSUmVMFy9exPbt26Gvr48dO3bA0NCwSpCZPXs2vvjiiyfyHvydh4cHfHx8nvh2qfZJd7HvvYQQUKvV+O9//4tx48ahqKgIAPDzzz9jy5YtcHJygp6eHjIzMwEAurq6AO6WLdDRuTt9AwMDlJaWIiYmBhUVFfD19QUA5UgOEdGzRKVSQU9PDwEBAXjllVfg6+urlByYPHky5s6di5iYGOzYsQNBQUEA7t74oK+vr2zDxMQE+vr6yncrcPdam7y8PLRt27ba/SYmJipVswMCAtC6dWut152dndGhQwfMmzcPa9euxXfffYcGDbT/3g4KClK+u5+kHj168MGrkpI+yFQyMzPDhx9+iEmTJgEA1q1bh4CAAHTp0gW7du1SLji7Hzs7O+Tn52PevHm1PVwiojpTeWrHzc0Nn3zyCT777DPlDtDWrVvD3d0dXl5eaNeuHVxcXAAAnTp1wuXLl5VtpKWlIScnB126dFEuBjYwMECTJk1w+fJl9OvXr8p+HRwcqlTNrnTlyhVMnDgRd+7cwYsvvoji4mL8+uuvePPNN7X6mZubP5H3gJ4dz8SppUoTJ07E+PHjAQCjR4/G5MmTMW7cODRv3vyhrn739vbGoUOHMGjQIPTv3x979uyp7SETEdUpHx+fKvWRJkyYgOPHjyvFcgFgxIgR2LZtGw4ePIjk5GS89dZb6NevHxwcHJQ+KpUKY8eOxeLFi/HXX3/h9u3bWLx4cZXraKrTuXNndOvWDf7+/ujduzeWLVtWJcQAtXeNDEmsDi80fiT33rX0NPCuJSKS3b13Lenr6yuvZWRkCAcHB2W5qKhIGBgYiNTUVKWtsLBQuLu7K3ccjR49Wnn973ctXb9+XfTq1UsAEDo6OmL8+PHi1q1b1d619HcajUa0b99eFBQUCBsbG3H79u1q59G7d2/x0ksvPZk35R47d+4UkyZNqpVtU+2RrkRBcXExDAwM4OzsDH9//1o/zPjnn39i4cKFSE1NxaVLl2p1X0RE/3aJiYlQq9Xo2LFjjX2EELh8+TJatGgBY2PjJ75/HR0drYuIqX6TLsgQERERVXqmrpEhIiKi+oVBhoiIiKTFIENERETSku45Mk+7+rVMLl++jNTUVBgbG6Nbt251PRwiIqJaJ90RmXurX5eUlGDmzJlwcnKCq6trlcdt/1sUFxdj9OjRyvKVK1cwcODAJ7qPffv24aOPPsKyZcue6HaJiIj+raQLMgBgZGSEkJAQmJubY9GiRWjcuDEiIiIQHh6u9QCl9PR0rfLzwN0gFB0dDY1Gg4KCAq0nVVaKjo5GcXGxslxWVoaoqKgqD3XKz89HQkLCQ487KCgIK1asqPa12NjYKjVFKtuLi4tRUVGBmJgYrScUV46rso7U+++/z1ohRERUr0h3aule69atw5UrV6q0L1u2DDt37lSeYfD777/DyckJdnZ2KCwsRFJSEjp27IiSkhK0bdsWP/74I7y9vREZGQk7Ozv89ddfCAwMhIODA8aMGYPOnTsjIiICP/74I9q2bYsRI0agZ8+eiI2NxYgRIzBw4ED4+flh69atAABbW1uEh4crtURUKhU6d+6M4OBgODs7w9DQUBnr3LlzkZ6ejuvXr+PVV1+Fl5cXvL29UVBQgJYtWyI0NBQODg4wNDREZGQkwsPDcfXqVUyYMAF9+vTBiRMnEBgYCGtr66fwjhMREf2L1OXT+B7F35/se+vWrWqf8pudnS26dOkiysvLhRBCDBo0SBw4cEBYW1uLrKwsIYQQxsbGoqysTBQUFIh27doJIYSYMWOGCA4OFkIIsWfPHjFu3Dhlm6mpqWLWrFli3rx5Ijk5WfTs2VMIIURWVpbo3r27EEIIOzs7cfPmTXHy5Enh4eGhNabi4mLRtWtXcf36dWFvby+ioqLEwIEDlddzc3PFpk2bhKurqzKWHTt2CCGEGD9+vAgNDRVCCDFgwABx7tw5MWbMGHH8+HEhhBAHDhwQU6ZMEUII8ccffwhPT89He3OJiIgkI/URGUNDQ6SkpCAnJ0erKmtaWhqMjIygVqsB3K1qmpKSAqBq9WsdHR2l39+ZmpoiNTUVGRkZeP3112FiYoKysjJlP3p6egDuFknLzs4GALzxxhvYsGEDbt++rRSvrCT+vyJtpqamWLhwId577z00bNgQAODp6YmsrCxYWVlpVZL9+1grx1hZqfvcuXMIDg7GgQMHAABubm6P81YSERFJSeogo6Ojg7Fjx+Krr77CN998g/Lychw7dgyOjo5ITExEeXk51Go1oqKi8Nprr/2jbR87dgy2trY4cuQI2rVrh19//RWBgYH466+/alxn0qRJePHFF9GgQQN88803NfYbPXo09u3bh0uXLuHOnTsICQnB9evXcfPmTezevfuhxmdvb49BgwbB1dX1H82LiIjoWSJ1kAGAb775BiNGjMCOHTugp6eHN954Ay4uLpgzZw7MzMxgYmICc3Nz9O3b96G2t3DhQixbtgzGxsb4/vvvoVar8f777+Ott95CTk4OkpKSaly3devW6NKlCywsLJSjKTVZvnw53nvvPRgYGMDKygoeHh4QQqC4uBg3b9584Dh9fX0xadIkGBkZIS8vD0FBQWjbtu1DzZGIiOhZIV2tpeLiYnTp0gXJyclKW0VFBaKjo2Ftba2c8nkU3t7eGDlyJIYPH/7I2xg/fjw+//xz2NnZPfI2Hse2bduwd+/ef+1t6ERERE+SlLdfZ2RkwM3NDYmJiQAAtVqNnj17PlaIeRLGjBkDCwuLOgsxfn5++PLLL+tk30RERHVBuiMyRERERJWkPCJDREREBDDIEBERkcQYZIiIiEha0gUZjUaD8PBwhIeHo6CgoK6HU6P4+HhkZWVptRUXF+PMmTNPZPtlZWXK+/D3ulBERET1iXRB5t7q1ytXrsRLL72EUaNGISwsDAAQGhqqPG33cZw/fx6xsbE1vv7RRx/VuJ9ly5bh1KlTWm1paWmYOXPmY48LAAoLC7FkyRKMGjUKaWlpT2SbREREspEuyAD/f/VrfX19rFmzBiEhIQgKCsLAgQMBAIsXL64SMDIyMrSePRMTE4O8vDytPteuXcONGzeU5YCAAERGRlY7hoKCAgQEBGDLli1a7Xfu3MG1a9e02oQQiI2NRUVFhVa7RqNBTEyM0n7v/oG7R3b+fmNZ5bK+vj5CQkJgaWlZ7fiIiIjqA6mf7NusWTOo1WocOnRICTFnz55FSkoK/Pz8MHz4cJSVlWHRokWwtLSEk5MTPD09MWzYMJiamiIqKgrz58/HK6+8gjlz5iAmJgYAYGdnh88++wwRERG4dOkSNBoNpk2bprXvgIAAzJw5E//9738xY8YMAHfLGrz//vvo27cvDh48iFdffRUA8Prrr0OtVmudCnNxcUGbNm1gYGCAr7/+Gt9++63W/pcsWQI3NzdYWVmhvLwcP//8s9YyH3hHREQEuatfCyFETEyMcHFxEaNGjRK3b98WQgjRv39/kZiYKIQQYt++feKll15S+q9YsUJ8++23Qoi7Fac7d+4sIiMjxciRI5U+AwYMEJcvXxaff/652LBhQ7XjcHR0FFlZWWLw4MHiwoULyn7z8vKEEEJ4enqKffv2iW3btoklS5YIIYRITEwU/fv3V/qGhYUJIUS1+z99+rQwMTERcXFxQgghMjMztZYr9ezZU5krERFRfSP1ERkA6N69Ow4ePIhVq1bhgw8+wObNm6v06dSpk/Lv6OhoVFRUwNfXFwAwbdo0nD17Frm5uUrboEGD0KhRoxr3eeLECajVapw7dw5WVlbYtGkTPvzwQ2RmZqJZs2ZafQ8dOgQnJ6dqt2Nubg4A1e6/TZs2WL9+Pd58802MHTsWs2fPrrJMRERU30kfZPLz89GsWTM4OzsrF/vq6OigsLCw2v52dnbIz8/HvHnzlLbIyEjo6upqtVVup6ioqMo21q5dC1tbW/z1118wMjLC+vXrsXDhQty6dQsZGRkwMjJS+pqZmeHkyZN4/fXXa5yDvb19tfvX1dVFSEgILCwsMGXKFHTv3l1ruXXr1g9+g4iIiJ5hUgeZiooKuLi4oFWrVsjJycGaNWsAAIMHD8bYsWPh6uqKMWPGaK3j7e2N4cOHIzw8HKWlpfjss88wbNgw9OjRA3369EGLFi3wwgsvYNGiRRgwYACmT5+Obdu2YceOHWjWrBlyc3Pxv//9D9euXUPjxo0B3L02JjQ0FAsXLkS3bt1gZ2eHW7du4dVXX8Ubb7yBAQMGwMHBASYmJtXOo1evXlX2P2vWLNjY2KBHjx7o3r071Go1rKyslGVDQ8PafXOJiIgkIF2tpXurX5eVlSE2Nha2trZo2LBhHY/u6Xvuueewfft2mJmZ1fVQiIiInjopb7/+e/VrXV1d9OzZs96FmJycHLi5uSEhIaGuh0JERFRnpDsiQ0RERFRJyiMyRERERACDDBEREUmMQYaIiIikxSBDRERE0pLuOTIajQaHDx8GAPTu3RtNmzat4xH9u1y+fBmpqakwNjZGt27d6no4REREtUq6IzKlpaVwd3fHkiVLcPv2bZSUlGDmzJlwcnKCq6urUkxx69at+OqrrwAAoaGhVaphP0hRURG8vLwwZMgQDBs2DNnZ2cjIyMDBgwfvu97D9HlYTZs2hUqlUn4uXbqEpKQkjBo1qsZ19u3bh48++gjLli17ImMgIiL6N5PuiAwAGBkZISQkBADw+eefo3HjxoiIiNDqM27cOOXfixcvxqZNm9CyZcuH3se+fftQVlaG/fv3K2379+/H77//jkGDBiltycnJUKlUylN7IyMjq/SpfGhf9+7doaur+9BjqKyWvXfvXgQEBKBr164AgKCgIKVPaWkp4uPj0b17dzRo0ADvv/8+2rdvj7179z70foiIiGQl3RGZe61btw6ff/65VltaWhpsbW3x9ddf4+zZs0hJSYGfnx/CwsIwZMgQxMbGAgACAwNrLL5oYWGBqKgoXL16VWnbu3cvoqOj4evrC41Gg+3bt+Odd97BvHnzMG3atGr7xMXFwdHRET/99BMcHR1x4cKFfzzH+fPnK3McO3Ys3N3dAQA2NjYYP348vvjiC/Tv3x95eXn/eNtERERSq9vi2/9cUVGRMDExEUIIcevWLeXf91qzZo346quvhBBC9O/fXyQmJgohhNi8ebP44IMPhBBCjBo1SkRFRdW4r+DgYGFhYSHmzp0rSktLxb59+4Snp6dWn9LSUnH8+HFhZGRUbZ8xY8aI48ePCyGEOHDggJgyZco/mu/vv/8uJk2apCzHxMSIoUOHCiGEsLa2Fnl5eUIIIebOnSv8/PyEEEL88ccfVcZJRET0LJL6iIyhoSFSUlKQk5Pz0Ou88sor+N///oe0tDRcv34dDg4ONfYdPnw4oqKicO3aNaxdu7bK6z///DMGDx6MoKAg6OnpITMzs0qfc+fOITg4GL6+vjh+/Djc3NweeqwajQbz5s2rcsSpOqampkhNTX3obRMRET0LpLxGppKOjg7Gjh2Lr776Ct988w3Ky8tx7NgxuLi4VOlXWFgIANDV1YWHhwfGjRuHN954A0IInD17Fj179tRaR6PRoKSkBC1atICDgwNycnKgo6ODoqIipc+6desQEBCALl26YNeuXRBCVOljb2+PQYMGwdXVVWv7Z8+eRY8ePaBWq2uc36ZNm+Do6Ahra+sHvhcREREYMmTIA/sRERE9S6Q+IgMA33zzDYKCgtC1a1c899xzOH36dJU+gwcPxtixY+Ht7Q0AeO2113D06FFMmDABubm5GDVqlNa1MABw/vx59OnTB25ubggJCYGXlxfs7e1x9OhRuLm54ciRIxg9ejQmT56McePGoXnz5oiIiKjSx9fXFx999BHc3NzQt29f3Lp1CwCwYMECbNy4scZ5aTQafPnll4iJiYGbmxvc3NywZ8+eKv3GjBkDZ2dntG/fHhMnTnyct5KIiEg60hWNLC4uRpcuXZCcnKy0VVRUIDo6GtbW1tDT03vgNmJjY+Hr64stW7YAAN5++22sXr26yh1Fubm5uHbtGrp37w6VSvVE57F8+XIMGzbsoY621MTGxgYnT55Es2bNtNq3bduGvXv3KreiExERPaukPCKTkZEBNzc3JCYmAgDUajV69uz5UCFm165d8Pb2xpdffgngbqiZPXt2tbdFt2jRAj169HjiIaa0tBS2traPFWJq4ufnp8yNiIjoWSfdERkiIiKiSlIekSEiIiICGGSIiIhIYgwyREREJK1nIsjcunULCQkJdT0MIiIiesqkCzL79+9Ho0aNMGLECLz22ms4fPgwQkND4e/vX9dDIyIioqdMuiAD3C2cGBwcjAULFmDSpElKe3JyMrKysrT6xsfH4/bt21pt5eXliImJ0WorKytDVFQUysrKlLb09HTk5+fXwgyIiIjoSZC6RIFarUZmZiaEENizZw+ys7Oxd+9e/PzzzxgyZAhee+01qNVqXL9+HR4eHpg9ezaef/552NjYID8/H1lZWQgLC0NcXBwmTJiAPn364MSJEwgMDIS1tTW8vLxgbm6OFStW1PVUiYiIqBpSBpkLFy7ggw8+wJYtW/Dxxx9DpVLBzc0NK1asQFBQEP744w8IIdCkSRNs2rQJxcXFaNWqFd5++23k5+dj+fLlaNWqFZ5//nncuHEDPj4+8Pf3R58+fRASEoKlS5di48aNWL16NRo3blzX0yUiIqIaSBlkWrdujbFjx2L69OlwcHDAb7/9pjyZt23btsjLy8ONGzfQrl07AICenh66deuGtLQ0AECjRo2Uvrdv31YqVB84cAAAlArVpqamT3tqRERE9A9IGWSMjIyqVJO+V5cuXbBz504Ad0sapKenw8TEpNq+NVWoTk9PR+PGjavUMiIiIqJ/Bykv9n0YAwYMQMOGDdGrVy+4uLjgs88+Q8OGDavtW1OFai8vL8yfP/9pDpuIiIj+AdZaIiIiImk9s0dkiIiI6NnHIENERETSYpAhIiIiaTHIEBERkbQYZIiIiEhaDDJEREQkLQYZIiIikhaDDBEREUmLQYaIiIikxSBDRERE0mKQISIiImkxyBAREZG0GGSIiIhIWgwyREREJC0GGSIiIpIWgwwRERFJi0GGiIiIpMUgQ0RERNLSqesBPCyVSlXXQyAiInqmCSHqegj/mDRBBgASEhLqeghEVI9YWlpK/73zLMyhtvC90WZpaVnXQ3gkUgUZkpsQAidPngQAGBsbo2PHjsqRtuTkZNy4cUOrv6OjI9RqNbKyslBQUABTU1Ot11NSUpCfnw8rKyuoVCplG7q6urC2tkbjxo2Vfjk5ObC1tUVcXBzy8vJgamqK9u3bAwBu3bqFpKSkKuNt37498vLy0Lx5c2XfZWVluHjxIoyMjGBsbKzM68qVK+jSpQsAQKPR4NSpU+jRoweaNGnyhN49+req/FxbW1ujRYsWAIAzZ86gY8eOMDIyAlDz5+be9U6ePImuXbtCpVIhPj4eAGBlZYWWLVsq+9NoNIiPj0fnzp3RsGHDpznVGlX+XjVr1gzdunWDWq3Wev3WrVto0qQJmjdvjjNnzqCsrKzKNnr37o1Tp05ptbVr1w4dO3as1bE/rsuXLyu/+5Xi4uJgY2OjLMfHx6Nbt27K8qN8HoqLi9G2bVsAQHZ2Nq5cuQJbW1tER0dXGVPz5s219v+sUwlJjiOpVComZ8lVVFSga9euyrKtrS2+//57mJubY9WqVfDz89PqHxMTg4ULFyIsLAzNmjVDr169sHz5cmRnZ2PmzJmIioqCsbExNBoN9u3bBz8/P2UbOjo6ePXVV/Hll1/ihx9+wNGjR7F582ZMnDgRx48fBwC0atUKX3zxBYqKivDFF19UGe/777+PqKgo2NvbY9asWTh+/DhmzpwJAwMDXLp0CUOHDsWKFSugo6MDBwcH7NixA507d0ZJSQlsbGywa9euevVl8g+wNmkAACAASURBVCx6mL/YKz/XAQEB6NOnDwCgX79+mDNnDkaNGnXfz82963Xv3h1+fn5o1KgRJk+erOzD0dER69evx8WLF/Huu+/C0NAQycnJ+O9//4sePXo89hwe199/r5o3bw4fHx94eHgor8+cORNWVlZ4++230a9fP6SlpVXZxoULF2Btba3V5uXlhU8//bTWxv24741Go0G/fv3g5+cHR0dHpd3NzQ0rV65E9+7dcf78eXz66acIDg4GgEf+PCxatAg7d+5E48aNcfjwYcyaNQtbtmzB4MGDq4yrb9+++O233/7xfCwtLaU8tcSLfempCwgIwPbt29G2bVu8++67qKioAHD3yz8hIUH5AYBt27bhl19+waFDh5QvtOXLlyM/Px+HDx/G/v37sWnTJuUvwJ49eyI6OhoLFizA5s2bcfv27Sr7/+ijj/DXX3/BxcUFq1evxvjx45GQkIA///wTABAWFoaEhATMnDlTWaesrAyfffYZpkyZgr1792Lv3r2IiorC5s2bAQCFhYV49913UVRUVHtvHEnnQZ+bB4mMjMSWLVsQFxeHsLAwHDhwAN26dUNwcDDCw8P/VUHZy8sLx44dw/Dhw7Fu3Tql/c6dOzhw4AD++OMPCCFw9OhRJCQkYNy4cRg7dqzy+175OxwQEKC01WaIeRLCwsJw+/Zt/PHHH0pbXl4erl69irCwMABAeHg4Lly4gOLi4sf6PFy5cqXKH1yVQSwhIQFmZmZYunQpEhISHinEyIxBhuqEvb09pk2bhosXLyIxMRHA3S+AEydO4MSJEygtLUXDhg1haWmJrVu3Ijc3F23atAEABAUF4a233oK+vj6Au4ef/65p06Zo2rQpGjduXOOh9/bt26Nr165KiHqQ6OhoXL9+HW+88QYAoGvXrnB1dUVoaKjSp6CgAD4+Pv/ofaBnR3x8vPL5rTx18jCfm/tp2LAhWrVqBZVKhWbNmsHS0hLnzp1DREQEDAwMqpzCqWtGRkbQ0dFBs2bNlLYdO3bg+eefR15eHo4ePfrAbVS+j1evXq3NoT4R27Ztw5gxYxAcHIy8vDwAQGxsLAAoQebQoUMA7n4WHufz0KhRIxw+fBhbtmyphZnIjdfIUJ2pPIyck5MD4O51MmvWrAEArFq1CoaGhti6dSvmz58PFxcXeHt7Y8KECSgoKFCuPbhXVFQULC0toVarsXr1aq1rCyrt2LEDx48fx5kzZzB//vyHGmtWVhYaNWqkdc2LpaWl1vnpRYsW4YsvvsDWrVsB8E67+mbnzp1o3rw5ACA/Px/Aw31u7qfytNGbb74JV1dXAIC+vj4+/PBDdOzYEd999x3Mzc2f4Cwenb+/P/z9/WFqagp/f3+l/ffff8f06dNhaWmJ33//Hc7OzvfdTuX76OLiAgsLi9oe9iO7ffs2Dh48iJCQEMTExOB///sfJk6ciPPnz8PKygrx8fE4c+YMzp07B2tra1y4cAHt27d/pM+DSqVCo0aNsGrVKrzzzjuYNWsWv1/+hkdkqM5U/uXSoUMHAICNjQ02bdqETZs2wdDQEADQsmVLrF69GrNnz8ayZctQUlKCxo0bV3txrkqlwnPPPYejR49CT08P5eXl1e63Q4cOGDJkCHbv3o3x48c/1Fg7dOiAkpISJCcnK23JyckwMzNTltu0aYOVK1fi22+/BSDnbYz06ObOnat8fg0MDAA83Ofm758TjUaDBg0aKP+TOnfuHGxsbFBSUqL0cXNzw65du1BWVoaNGzfW8qwejkqlwowZM/DTTz8hKysLTZs2BQCcPn0aly5dwqVLl3Dnzh3s378fmZmZ991W5fv45ptvPo2hP7Jt27ahefPm2LVrF1q2bInAwEAAd6/16du3L4YMGQJfX1+4ubmhX79+iI2NfeTPgxACQgg4OzvDy8sL3333Hb9f/oZBhp66rKwsxMXFYdmyZXByclKu2q/OiRMnAEC5a6ioqAhDhgyBn58fTp06hczMTPj5+WndBWFsbIzZs2fD19cX2dnZVbb5/PPPY/Lkyf/oL1lra2uYmZlh1apVyMzMxJ49e7Bt2zaMGzdOq5+TkxPeeeedh94uPdvu97lRq9Xo1KmT8hk/duwYSkpKtD6XDRo0wNKlS7F582acPn0aKSkpuHHjBpo0aYJWrVopR37+LQYPHoz+/fsr17b88ccf6NmzJ/T19WFhYQFjY2Ns3769jkf5+IQQ2Lp1K/r16wcAeOGFFxAXF4ezZ8/i/PnzsLW1xZgxYxAdHY2xY8eiW7duiImJeezPAwC8++67WhcWE08tUR348MMPYW5uDkdHR63/6R89elTrOQaXL1/Gl19+ibi4ODRq1AiffPIJzMzM8PHHH8Pb2xvjx4+HWq2Gu7s7cnNzlb9aAGDKlCnYtWsXfH19n8ihd5VKBR8fH3z00UfYuXMn2rZtq5zyuvc6G29vb0RGRj72Pkl+9/vcAMCCBQswe/ZsrF69Gnp6evDx8UHHjh2RkpKibMPW1hbTpk3Dp59+ivfeew9z585FeXk5unfvjgULFtTV1LT8/Xdv0aJFcHNzw6ZNmxAcHIyvv/4aI0eOBACUl5cjMDAQb731Vo3bev3115V/f/TRR3jvvfdqd/CP4NixY0hJScHWrVvRunVrAHdvlQ4ICMDly5dha2uLrl27olu3bhg4cCAuX76My5cvo6Sk5LE/DyqVCt9//73WXW31HW+/pn81jUaDixcvwtTUVOsCQiEEkpKS0KxZM+WL5GkoKirC1atXq31WBj17ntSty/f73JSWluLixYuwsLBQTsncT3Z2NtLS0pTnJz0IH/pWs7p6b57k5+FJkvX2awYZIqIaPAsh4FmYQ23he6NN1iDDa2SIiIhIWgwyREREJC2pTi0RERFR7ZEkEmiR6q4lGd9gIpKXSqWS/nvnWZhDbeF7o03WAwY8tURERETSYpAhIiIiaTHIEBERkbQYZIiIiEhaDDJEREQkLQYZIiIikhaDDBEREUmLQYaIiIikxSBDRERE0mKQISIiImkxyBAREZG0GGSIiIhIWgwyREREJC0GGSIiIpIWgwwRERFJi0GGiIiIpMUgQ0RERNJikCEiIiJpMcgQERGRtBhkiIiISFoMMkRERCQtBhkiIiKSFoMMERERSYtBhoiIiKTFIENERETSYpAhIiIiaTHIEBERkbQYZIiIiEhaDDJEREQkLQYZIiIikhaDDBEREUmLQYaIiIikxSBDRERE0mKQISIiImkxyBAREZG0GGSIiIhIWgwyREREJC0GGSIiIpIWgwwRERFJi0GGiIiIpMUgQ0RERNJikCEiIiJpMcgQERGRtBhkiIiISFoMMkRERCQtBhkiIiKSFoMMERERSYtBhoiIiKTFIENERETSYpAhIiIiaTHIEBERkbQYZIiIiEhaDDJEREQkLQYZIiIikhaDDBEREUmLQYaIiIikxSBDRERE0mKQISIiImkxyBAREZG0GGSIiIhIWgwyREREJC0GGSIiIpIWgwwRERFJi0GGiIiIpMUgQ0RERNJikCEiIiJpMcgQERGRtBhkiIiISFoMMkRERCQtBhkiIiKSFoMMERERSYtBhoiIiKTFIENERETSYpAhIiIiaTHIEBERkbQYZIiIiEhaDDJEREQkLQYZIiIikhaDDBEREUmLQYaIiIikxSBDRERE0mKQISIiImkxyBAREZG0GGSIiIhIWgwyREREJC0GGSIiIpIWgwwRERFJi0GGiIiIpMUgQ0RERNJikCEiIiJpMcgQERGRtBhkiIiISFoMMkRERCQtBhkiIiKSFoMMERERSYtBhoiIiKTFIENERETSYpAhIiIiaTHIEBERkbQYZIiIiEhaDDJEREQkLQYZIiIikhaDDBEREUmLQYaIiIikxSBDRERE0mKQISIiImkxyBAREZG0GGSIiIhIWgwyREREJC0GGSIiIpIWgwwRERFJi0GGiIiIpMUgQ0RERNJikCEiIiJpMcgQERGRtBhkiIiISFoMMkRERCQtBhkiIiKSFoMMERERSYtBhoiIiKTFIENERETSYpAhIiIiaTHIEBERkbQYZIiIiEhaDDJEREQkLQYZIiIikhaDDBEREUmLQYaIiIikxSBDRERE0mKQISIiImkxyBAREZG0GGSIiIhIWgwyREREJC0GGSIiIpIWgwwRERFJi0GGiIiIpMUgQ0RERNJikCEiIiJpMcgQ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", }, { - name: "Web-Invoice-2", - template_id: 3002, + name: "Mobile-Invoice-1", + template_id: 1001, ImageUri: - "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UwkJCUoppebOnauMjY2Vi4uLsrGxUaampmr16tVa+ylTpqgBAwZor+Pj45Wbm5uqXLmy9l8fHx+llFJ79uxRxsbGytPTU+fnzJkzeR6Pi4uLSk5OVtHR0apfv34v9DsRoqSU8VgpFgZ5RvqsTz75BE9PT1xdXXWWGxkZMWTIEDZv3szs2bO15du3b6dPnz5UrVqVEydO8NlnnxEQEEDHjh0BmD59OhMnTsTLy4v69evn2N+kSZPIzMwkKioKKysrUlNTiY2N1dabmZmxf//+Ao09ISGBypUrY2ZmxvHjx+nQocOL/AqEEMWoXFwjdXZ2ZtSoUfy/9u48Kss6///4CwSFEEiR3Cj30tHMssW9LGwqLZvS1MKac1pmWqapppk60zfbbdoszcpGMUsr7dSxRc3lGE67hokRYcqUCyKKy1FIEYT37w+P92/ubjDkDUHwfJzDOXJd1+e6P9fNfT+97+u6xcLCwpB1KSkpWrdunVatWiVJOnDggN577z1dddVVkqTnnntOl19+eSCikvT4448rKSlJr776asj+vvvuO82ZM0fPPPOMEhISJEnNmjVT+/btj3neM2bM0IUXXqgffvhBw4YN0/3336833nhDixYt0uLFi9W+fXstXbr0mPcLoGY1+JCGhYWpR48eGj9+vG666aaQ9T169FC/fv00d+5cSdKCBQvUpEkTXXrppZKktWvXatCgQUFjIiIidPbZZ2vDhg0h+1u7dq1iY2ODwvtzpaWleuSRRwJfJSUlFW53ww03aMCAAXrllVe0bNkytWzZUunp6brkkkvUrFkz5efnq1mzZlW+LwDUjgYf0iPuuecebdq0Sa+99lrIunHjxumtt95SWVmZ3nnnHY0ePVqRkZGSpN27dysuLi5kTFxcnIqKikKW5+fnq127djU2708//VSDBg3S9u3b1aZNG4WFhUmShg4dqrKyMp177rk1dlsAqqfBnyM1M0lSeHi4Zs+erUGDBun2228PBEk6HNK7775b7777rt577z0tXLgwsO7EE09UXl5eyH43b96srl27hiw/4YQTtG3btqPOKTIyMuicbEV++OEHzZkzR7m5uXryySeVk5OjwsLCQFgB1B+N5hWpJHXp0kUTJkzQww8/HAisJCUmJmr48OG644471Lp1aw0dOjSw7pxzztHixYuD9rNz506lpaXp4osvDrmNPn36aN++ffrggw9cc23RooUOHjyoUaNGafDgwcrPz9cNN9xQ4cUtAHWrUYVUkm6++eYKz1+OGzdOubm5Gj16dNCr1bvvvlurV6/Www8/rL179yorK0ujR49Wnz59dNFFF4Xsp2fPnrrjjjt0/fXXa8GCBSoqKtJnn32miy66SD/99JMkqby8XCtWrAj6+vlpghYtWmj37t0aN26czjvvPO3evVtjx44NhDQzM1PXXHONsrKyavLuAVANjS6kkvTqq6+GnPe87LLLdPzxx2vs2LFBy7t27aqPPvpIb7/9to4//nidccYZateunT788MNK9z9p0iT95S9/UUpKimJjY3XNNddo2LBhgQtDxcXFGjp0aNBXTk5OyH5Wrlypc845R+vXrw/6BwCStGXLFs2dO1e5ubnVvRsA1JAw+9/3uPVMWFiYamt6W7Zs0Yknnhi0LCMjQ3369Kl0zLp165SUlKTmzZtX6TZKS0v13XffqXfv3kGvcqvCzLR69WqdeeaZ2rVrl4qKitShQ4egbbZv367WrVsf036B2labz9v6qtGGFEDtaIzP20b51h4AahIhBQAnQgoAToQUAJwIKQA4EVIAcCKkAOBESAHAiZACgBMhBQAnQgoAToQUAJwIKQA4EVIAcCKkAOBESAHAiZACgBMhBQAnQgoAToQUAJwIKQA4EVIAcCKkAOBESAHAiZACgBMhBQCniLqeQE0rKyvTJ598IkmKjo5W9+7dFR8fX+G269evV3R0tE488URlZ2dr+/btFW7XqlUr9ejRI7Df/9WxY0d17Nixxubv9fHHHys9PV3nn3+++vTpU+E233zzjZKSktSyZUuZmT788EOtW7dOgwcP1llnnVXhmB9//FGdOnWqcN2+ffuUk5OjM844Q5K0bds2vf/++0pISNDw4cMVHR0tSSosLNTq1auDxnbr1k3t27ev7uEC9YPVY9WZXmFhoUmyc845x84++2yLjIy0yy67zH788ceQbW+55RZ78MEHzcxs4sSJlpycbMnJyda2bVvr0KFD4PsJEyYE7ffI8uTkZJs1a5b3MGvMPffcY6effrr96U9/sujoaJs+fXrINiUlJdaxY0fbvn27mZm9+eabdvrpp9tdd91lPXr0sPvuuy9kzEcffWQdOnSo9HYnT55sd911l5mZLVmyxKKiouzqq6+20aNHW+fOnW3r1q1mZvbV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8cfrwgsvVFZWlr744guNGTNGkydPVuvWrXXfffdpzpw5Gjt2rN58801J0uOPP67bbrtN5557rlq1aqUxY8Zo586dkqS//e1vKi4u1j/+8Q8NGzZMBQUFIeMladasWerZs6datmypUaNGqaCgQJK0aNEiPfHEE7rtttvUsmVLDRw4UBkZGb/afVtXzEzTp0/XtddeG3Q/SVJKSooWLFigXr16qV27dnrooYc0ceJEdezYUb169QqK3KpVqzR48GDFx8dr0KBBWrVqlSTp+++/17Bhw4L2e/XVV+vTTz/V1q1bde211+rf//63OnfurJNPPlnz5s2TJGVkZGjmzJnKyMjQsGHD9Mwzz9TyPdGIWD1W1enV88MIUlhYaJLsiy++CFoeFxdnqampVlxcbKeeeqpNmjTJduzYYbNnz7bY2Fhbu3atffLJJybJ0tPTLSMjw9566y0zM3v++ectKSnJlixZYnv27LHU1FQrKiqyBQsWWNOmTe26666zgoICKygosI0bN9rYsWPtuuuuMzOz6667zvr162cbN2601atX28CBA23o0KFmZrZmzRqLioqyyZMnW1pamhUXF4eMnz9/vjVv3tzmz59v3333nV1wwQU2YMAAKy8vt9TUVIuOjraFCxdabm6uXXPNNda/f/9f7b72qu7javHixda9e3fbtm2bRUREWG5ubmBdp06dbPDgwZaVlWWTJk0ySXbnnXfajh077O6777YWLVrYgQMHbMuWLRYXF2cPP/ywbd261f785z/b8ccfb3l5efbVV1+FzK1Tp0727rvv2oYNG0ySTZw40Xbs2GFPPvmkRUdH286dO23Pnj12880321lnnWVpaWm2bt26Wrk/fkvPx5pSr4+4If7gKgtpQkKCTZ8+3V577TXr2bOnpaWlBb5GjBhhd955p61fv97Cw8Pt8ccft/Ly8sDYLl262CuvvBJyW0dCunv37qDld911V1BI77rrrsC6zMxMk2QbNmwwM7PjjjvO0tLSKh0/cuRIu/XWWwPrVq1aZZIsMzPTUlNT7ayzzgqsW7FihTVv3rzK91Vdq+7javTo0fbggw+amdn5559vEydODKw7ErwjmjdvHrh/8/PzTZJlZWXZ008/bb169QpsV1ZWZi1atLAXXnihSiHds2dPYFxERIR9+eWXZmb2xBNPWHJycrWOqyE+H2sKb+3rgS1b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", }, { - name: "Company-Invoice-1", - template_id: 3003, + name: "Mobile-Tax-Invoice", + template_id: 1002, ImageUri: - "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAEklEQVR4nGP8z4APMOGVHbHSAEEsAROxCnMTAAAAAElFTkSuQmCC", + "iVBORw0KGgoAAAANSUhEUgAAAVIAAANACAYAAAB5VOVWAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AAAAtdEVYdENyZWF0aW9uIFRpbWUAU3VuIDMxIEF1ZyAyMDI1IDAxOjE3OjIyIEFNIElTVPC8pOQAACAASURBVHic7J13eFTF+sc/55zdzaYHCAmE0KTG0EEEIk2MggjSFFBEsPysoNi4XNR7raAoyiMqXilyQVC6gnLpiBCKKEFapEgRpIeSstl25vfHZk92k00ILAqB+fDsk91zZt6Zs8A378y8846Sm5srdF1HCIGu6+i6TmGEEEWuSSQSicSDyWQy4RVS8IimFE6JRCIpPSZFUdA0DcBPTCUSiURSOkyqqhofpIBKJBLJxWNSFMX44PteIpFIJKVDleIpkUgkwWGC0nmictgvkUgkgTGVtqD0XCUSiSQw6oWLSCQSiaQkpJBKJBJJkEghlUgkkiCRQiqRSCRBIoVUIpFIgkQKqUQikQSJFFKJRCIJEimkEolEEiRSSCUSiSRIpJBKJBJJkEghlUgkkiCRQiqRSCRBIoVUIpFIgqTU2Z8k1x4ZGRkcP34cgPbt2xvX165dS/Xq1alatWrAekeOHGHPnj0A3HTTTSiKwqZNmwCIiIigRYsWxba5evVqAEJCQmjdurXfPSEEe/bsYevWrRw6dIjatWuTnJxMrVq1AmYfO3bsGL/99hsAycnJxMbGBmwzMzOT9PR0tm7ditlsJiEhgebNm1O9enWjzOHDh9m7d2+x/fbSsGFDKlSocMFykusMIbluGTRokAAEIE6dOmVcj42NFXXq1BFnzpwJWO/jjz826m3btk24XC6RkJAgAGG1WkVWVlbAemlpaUa9++67z+/e8ePHxc0332zc9301btxY7Ny5s4i9SZMmGWXmzp1b5L7T6RTPPPNMQJuqqvr1Ydy4cQHLFX4tWrSoVN+t5PpCDu0lAdmzZw8DBgwoVUJvTdO47777AMjLy2P27NkBy82YMcN4/+CDDxrvt2/fTsuWLdm4cWPAelu3bqVZs2bMnTu31P0/deoUHTp0YNy4cQHv67pO7dq1S21PIikJKaSSYvnuu+944403SlX2/vvvN97PmTOnyH2n08nXX38NQI0aNUhNTTXuPfbYYxw8eBCAN954gz179pCXl8eOHTt44YUXAI9AP/vss+Tl5ZWqP8OHD2fdunUAxMXF8eGHH7J3715OnjzJ2rVrefbZZ3nmmWcC1h07diyrVq0K+GrVqlWp2pdcZ1xpl1hy5ShpaI/PEHjp0qV+9QoP7b00atRIAMJsNovMzEy/OvPnzzfq/Otf/zKuz50717j+8ssvB+znsGHDjDKjR482rhc3tPedQkhKShJ//vnnBb8L36H98uXLL1heIvFFeqSSEtF1nfvvv9/wGEti0KBBgL/36WXmzJkAqKpqlPO9brFYePrppwPaHTZsGN5jw+fPn3/BfkydOtV4//rrr1O5cuUL1pFIgkEKqaRYvKvaJ0+epE+fPtjt9hLLDxgwALPZDOA3T3r27Fm+/fZbAG677TZq1Khh3Nu+fTvgWQ2Pj48PaLdq1arceOONAOzfv/+C/d62bRsAVapUoU+fPhcsX5hff/2V1atX+71OnTp10XYk1w9SSCVF8IYatWnThmHDhgGwefNmHn/88RLrVaxYkS5dugCeMCdvaNXXX39tzG0OHDjQKJ+bm0tGRgYACQkJJdquUqUKACdOnCA3N7fYcrqu88svvwBQp06dEm0Wx3PPPUfHjh39Xt75VokkEFJIJSXyzjvvkJKSAsAXX3zB559/XmJ57+q9ruvGKr13+B4dHU3v3r2Nsl6hBYiKiirRru/9M2fOFFvuwIEDhmhXqlSpRJsSyeVCCqmkCCI/5EkIgdls5uuvv6ZixYoADB06lPT0dKNs4UD5u+++m5iYGADmzp3L/v37+eGHHwB44IEHsFqtRtmqVasac5++ohqII0eOAGA2mw3vNBC+86EXslkcy5cvRwjh97r77rsvyZbk+kAKqeSCVKlShWnTpgGeMCRfr1QUijO1Wq30798fgHXr1jF69Gjjnm/sKIDJZKJatWqAZ5dSSfzxxx8A1K9fv8RyoaGhxtxuaeZTJZLLgRRSSam44447GDFiRKnKeof3AP/5z38Az2JSoK2jXtHLyMgwxLIwW7duNe7dcMMNF2zfW+bAgQPG1lWJ5K9ECqmk1Lz++ut07NjxguVuueUW6tWr53ftkUceCVjWO2R2uVz8+9//LnI/KyuLZ5991vj80EMPXbD9vn37Gu+HDRvGuXPnipTZuHEjK1asuKAtiaQ0yKQlklJjMpmYOXMmTZo0ueBQfMCAAbzyyiuAZ7hfeFjv5YknnmDcuHEcPHiQyZMnk52dTbdu3ahQoQK7d+/mP//5Dzt37jRsdu/e/YL9fOSRR/j000/ZunUraWlpNG/enKeeeoqkpCTOnTvH7t27GT16NHXq1OHnn39G0zS/+l9++SVpaWkBbT/66KNyEUtSlCu4GUByhbnQzqZ+/foFrLdy5UqhqmqRnU2+/P7774bt4ux4WbVqlYiLiysxWUi/fv1Edna2X72Skpb8+uuvol69eiXajIyMFOnp6UKI0ict2bJlywW/V8n1hxzaSy6ajh078q9//avEMjVr1qRTp05A0UWmwnTo0IEtW7bQs2dPY8UfPLugkpKSGDNmDDNnziQ8PLzUfWzYsCG//PILw4YNKzKvmpiYyIABA9i1axeNGzcutU2JpDgUIUqR3kciKYSu66xZs4abbrqpWIHbv38/Bw8epG3btkWGzyVx4MABTp8+TXJysl+4VDCcP3+eXbt2UadOHcqXL39ZbEokXqSQSiQSSZDIob1EIpEEiRRSiUQiCRIppBKJRBIkUkglEokkSKSQSiQSSZBIIZVIJJIgkUIqkUgkQSKFVCKRSIJECqlEIpEEiRRSiUQiCRIppBKJRBIkUkglEokkSKSQSiQSSZBIIZVIJJIgkUIqkUgkQSKFVCKRSIJECqlEIpEEiRRSiUQiCRIppBKJRBIkUkglEokkSKSQSiQSSZBIIZVIJJIgkUIqkUgkQSKFVCKRSIJECqlEIpEEiRRSiUQiCRIppBKJRBIkUkglEokkSExXugMSSbCsWLGCtLQ0AOLj42ncuDE333xzwLJffPEFd9xxB5UrV2b+/Pls3749YLno6GiefPJJRo0aVeRe/fr1ueeeey7fAwQgLy+PcePGsWnTJpo3b87QoUOJiIgoUm7z5s0cPHiQ3r17AzBr1iy++eYbLBYLffr0oWvXrkXqZGZmsmXLFjp16hSw7cI2v/32W+bOnUtoaCj33Xcf7dq1M8q+++672O1243N8fDz/93//F9Szl0WkRyop86xcuZJx48aRmZnJunXr6NGjB0OGDMHtdhcp+9577zFjxgwAMjIyWLNmDWvWrGHSpEl8/PHHxueffvoJl8vFq6++yjfffGNcX7NmDbt27frLn6l///7MmDGDGjVq8PXXX9O/f/+A5d555x2ys7ONz//9738JDQ3l0KFD3HXXXUyePNmv/LFjx7j99ttZunRpsW372pwyZQqPPfYYkZGR5OXl0alTJ7799luj7JgxY9izZ08wj3ptICSSMs4///lP0bZtW+NzVlaWSEpKEp9++qlfuR9++EFYrVbRtGnTIjaeeuop0aNHD79rNptNAOJ///vfX9PxYnA6nQIQs2bNEkIIsWzZMgGIvLw8v3LHjx8XcXFxIisrK6Cd2267Tdx+++3G5zlz5oiKFSuK6Oho8dJLLwWsU9hmTk6O2LFjh3G/c+fO4r777jM+x8bGikWLFl3ag15DSI9Ucs0RERHBI488wltvveV3ffLkybz44ovs3buX9PT0oNt59913qVGjBnv37g3ali8mk4m2bdvy9ttvc+DAAVatWkVSUhIhISF+5RYsWEC3bt0CDvkBXC4X1apVMz6vX7+eb7/9lmbNmhXbdmGbYWFh3Hjjjcb9s2fPEh8f71dn06ZNvP/++6xYseKin/VaQQqp5JokOTmZw4cPc/z4cQDOnTvH7NmzefDBB+nSpQvTp08vta2RI0eSmppKamoqCxYsMK7HxcXRpEkTzGbzZe//tGnTOHLkCC1btuSTTz7h888/L1Jm9uzZDBgwoMj1vXv3kpKSgsPh4L333jOuv/fee7Rq1arEdouzCZ650w0bNvDAAw/4Xd+xYwfp6el0796dhx56qDSPd80hhVRyTaKqnn/aMTExgGfusGHDhtSqVYtu3boxc+bMgHOogWjQoAHt2rWjXbt2JCQkGNcHDRrEggULqF69+mXtuxCC1157jcjISN544w1iYmKYOHEiuq4bZX7//XcyMjJo3759kfqqqlKuXDn27dvH//73v1K3W5JNgJdffpk+ffrQtGlT49rWrVuZM2cO06ZN491332XKlCmcOHHiIp722kCu2kuuSQ4cOEBiYqIxHP7ss88Mb8nrOS1ZsoQ777zzgrb69+/PHXfc8Zf215eVK1cyZcoUdu3aRf369WnSpAmtWrXiiSeeoGXLlgB8+eWX3HvvvSiKUqT+DTfcwKJFixgzZgxPP/00ffr0QdO0C7Zbks3JkyezcePGIlEOvr9YUlJSADh48CBxcXEX9cxlHemRSq45cnNzGT9+vDFETUtLY8eOHTz//PMoikJ0dDROp9NYvb/aOHLkCJUqVaJ+/foA3HzzzSQkJBjRAkIIZsyYUWSIXZjExEROnTpFTk7OBdssyeaRI0d4/vnnGTNmDFWqVCnWxqFDhwCoVKnSBdu71pBCKrkmOH/+PKtXr2by5Ml06NCByMhIXnnlFcATwnPrrbcihDBeM2bMYO7cuX6hQ8Wxbds2Vq9ebbx2794NwMKFCxk4cCCnT5++rM/SpEkTjh07xocffsjx48cZM2YMf/75p7FItHHjRiIjI2nSpIlfPbfbzWuvvcaBAwfYuHEj48ePp1GjRkRFRV2wzeJsAjz++OPExcVRu3Zt4zs4deoUAKNGjWLbtm3s2rWL//znP7Rq1YqqVatehm+hjHElQwYkksvBP//5TwGIqKgo0blzZ/HRRx8Jt9sthBAiOztbhIWFiQkTJvjVOX/+vAgNDRWTJk0SQpQc/lT49dRTTwkhhBg+fLiIiooS6enpl/2Z5syZI5KSkgQgLBaLeOONN4x7zz33nHjnnXeK1Dl16pRo3bq10c9atWqJdevWFSnXsWPHIuFPxdn88ssvA34HCxYsEHl5eaJp06YCEKqqih49eoht27ZdhqcveyhCCHEF9FsiuSY4evQolStX/ktsCyFIT0+nbt26hIeHl6qOy+Vi/fr1VKpUidq1awec77zc7Nu3D6vVWuKw/1pHCqlEIpEEiZwjlUgkkiCRQiqRSCRBIoVUIpFIgkQKqUQikQSJFFKJRCIJEimkEolEEiRSSCUSiSRIylTSkpdffpmHH36YmjVrsmLFCho0aOCXG/HPP/9k0qRJjBgxgokTJ5KQkED37t39bEybNo2wsDDjGIUvvviCFStWkJ2dTf369XnmmWf89gr7tullwoQJrFu3DpvNRpMmTejduzdJSUl+R1748thjjxWbxGHlypX88ccfPPjgg3z88cekpKQE3KYnkUiuXsqMR5qTk8Nbb71lJEZYuXIlgwYNwnc/wZ9//smrr76Ky+Vi3759DBs2zM+GzWbjqaee4uzZswCMHz/eOF+mZs2azJ49m5tuuol9+/YFbBPghRdeYNiwYSiKQkJCAnPmzOH33383+vTBBx/4HUuxZs2aEpNGfPfdd8Yvg1mzZlG7du1gvyqJRPJ3cyX3p14M2dnZAhCrV68WQhTsrx49erRR5qeffhKAsNlsYsuWLQIQa9euNe7PmTNHWK1WkZmZKQ4ePCgsFouYOXOmcf/06dMiOTnZOEqhcJsrVqwoYtOXwkdelIbWrVuLrKwsYbPZLrquRCK5OigzHmkgGjVqxFtvvcX69euL3GvSpAmNGzdm1qxZxrV58+Zx1113Ua5cOaZOnUqVKlXo27evcb98+fK8+OKLzJs3zy+JrpcPPviA3r17G3kXg+Hnn3/mlltuYf/+/fTs2ZOWLVty6tQpHn/8cQB69epV7EmYEonk6qLMCqmiKFSvXp3333+fAQMGGMN1Xx544AG++uor3G43NpuNhQsXGsK5b98+GjRoUCSpQ506dcjLy+PIkSNF7GVkZNC8efMS+7V9+3bjWIo+ffoUW6558+a8+OKLDBo0iGXLltGtWzfee+89JkyYAECNGjWMfJQSieTqpswKqZdHH32UpKQkBg0aZIii9+fAgQM5deoUy5cvZ+HChWiaxl133QVAVlYWkZGRReyFhoYCBJzXPHfunHF0RXHExMQYx1Jc6HyctLQ0w7tNS0ujbdu2xr2xY8cyderUEutLJJKrgzK1au+LyE/QC57EvU2aNDG8OS8VK1akS5cuzJo1i5ycHHr37o3VagWgatWqAU+SPHDgAADVqlXzW8gCj5fovV8ciYmJRkLh4tB1nbfeeouFCxfidrvZsmULO3fuZMqUKQwdOrTEuhKJ5OqjzHuk4BHML774gokTJwL4CWD//v2ZP38+3333Hf379zeut2nThrS0NI4dO+Zna8GCBXTu3JmwsLAi7dxyyy18/fXXuFyuoPqrKArJyclERERw1113oWka3bp1M87jkUgkZYtrQkgBUlNTefrpp4tc7927N06nk+joaDp06GBc79OnD02aNOH+++8nIyODzMxMxowZw4wZMxg5cmTANv75z3+Sm5tLnz59yMjI4Ny5c0yYMMEvzMp75IXvy+Fw+NlRFAVd1+ncuTMdOnTg7Nmz9OvXz28q4J133uGll14K8luRSCR/B9eMkAKMGTOmyAKN1WrlnnvuoW/fvn4nKaqqytKlS6lQoQLJyclUqFCBadOmsWTJEm655ZaA9mNjY9m4cSNZWVkkJSURExPDV1995Rf0v3XrVjp27Oj3yszMLGJr1apVdOrUCSg6Pwrw7bffMnv27Ev+LiQSyd/HNZch//DhwyQkJBjnmoPnOAiTyUTFihUD1jlz5gyZmZnUqlWr1O2cPHkSu91OYmLiJfUzPT2d5ORkzGYzmzZtKjKsz8vLw263Ex0dfUn2JRLJ38c1J6QSiUTyd3NNDe0lEonkSiCFVCKRSIJECqlEIpEEiRRSiUQiCRIppBKJRBIkUkglEokkSKSQSiQSSZBIIZVIJJIgkUIqkUgkQSKFVCKRSIJECqlEIpEEiRRSiUQiCRIppBKJRBIkUkglEokkSKSQSiQSSZBIIZVIJJIgkUIqkUgkQSKFVCKRSIJECqlEIpEEiRRSiUQiCRIppBKJRBIkUkglEokkSKSQSiQSSZBIIZVIJJIgkUIqkUgkQSKFVCKRSILEdKU7cDF0794dm81W5PrYsWOxWCw8/fTTAJhMJpKSkujQoQOpqamEhob6lT948CBjx45l8eLFAHTp0oXnnnuO6tWrG2VmzZrF559/zqBBg7j//vv96j/zzDPs3LmTTz/9lNq1a1/ux5RIJGWMMiWka9asoUuXLtx4441+16Ojozlx4gTLly/npZdeIiIigpycHB5//HE6derEtGnTjLKrVq2iV69e3HnnnbzxxhsAfPPNNzRt2pR58+bRoUMHAA4fPswPP/zAgQMH6Nu3LyaT56vatm0bn376KU6nk+zs7L/nwSUSydWNKENER0eLr776KuC9n376SQDi6NGjxrXFixcLQBw/flwIIURubq6oXr26GDJkSJH6Tz75pKhZs6aw2+1CCCHGjh0rbr31VlG7dm0xffp0o9zDDz8sHn74YQGI9PR04/qZM2fEqlWrxMmTJy/Ls0okkrLDNT1H6h3SK4oCwMqVKzl48CAjR44sUnbEiBHs37+fZcuWGdc0TeMf//gHo0ePRgjBkSNHmDdvHsOGDStSPz09nY4dO7Ju3bq/6GkkEsnVSpkT0lGjRpGammq8Pvvss4DlTpw4wZtvvkmXLl2oWLEiAL/99htxcXHEx8cXKZ+YmEhUVBS7d+/2u/7ggw+SlZXF999/z/jx43n00UeJjo4GQAhhlKtRowavv/469erVu1yPKpFIyghlao4UoGfPnrRv3974XLVqVb/7lStXNt4//vjjfPjhh8Znh8NBTExMsbYrVqyIw+EAPCIphMBkMjFixAhGjRrFoUOH2Lx5s1HGlxo1avDKK69c8nNJLo1h7d1XuguSIPngB+1KdyFoypyQ1q9f31gQCsS8efOIiYnhhRdeYPv27X734uLiOHToEEIIY7jvxeVycejQIT8h9jJ48GBGjRpFnz59iIuL4/Dhw5flWSQSybVBmRvaX4jWrVvTsWNHvvnmGw4ePMigQYOMIXjXrl0BmD59epF606dPJzQ01Cjji8ViYcmSJbz66qt/beclEkmZ5JoTUi+JiYksWrSI7777zhhyx8fH88wzz/DYY48xfvx4Tpw4wdGjR/n444958skneeaZZ6hQoUJAe/Xq1StxWiA9PZ3U1FTWrl37lzyPRCK5eilzQ/uLoVGjRsycOZMePXqQnJxM//79ee2118jLy2PIkCEMGTLEKDts2LCAq/ml5ezZsyxfvtzYFCApm3jmxt0I9CvdlYtA8fxRNDwzVsqFKkguM4rwXXq+jjh27BgZGRmAx9sMNDcqufq5nItNQgh04ULgQlccoOjA3/ffQ4j85vKbFAG1XEFVTAh0FDQEOppiBkU17iuoqGVIVOViUxmmUqVKVKpU6Up3Q3IVIPIVTBduBE7M4TlE18jFHOYuqkPC70dJVinORRFCgBBFbejgsnkEVXd53nsqKLjzQ76FAFwFoqsoGqoIweVUcdpVdKcJFQtCqCiKKV9Qr34xLetct0IqkYC/iOrCiVvJIb7GcVp2hQqVTKD4xwsD6LpeKkdV1/WAxYSu43J5bBS5L8DtErh9nWEBDpcFgYpwC1w5Al0XoIOuC1wuQdZ5lcxjFs7+aSX7pBW3zYKqhyB8xFRRrtklkSuOFFLJdY8QOkLouHUnLiUHRXViMquYLAqK4hErAK9j59XVQLNiHu9PGHXA49QW9QlVBOAp5qlT7CSbALfbiaop6G6PN6soCrrLjdutowvQdbA7HGSesHN4Vw7HdoWSfdqKqlsRWFExoRr9kx7q5UYKqeS6xyNoLlx6Lnnuc9gdeSDCPAKoKGia4ldYFwJ7npu8XKfHe3QV3FZUBWu4Qmi4ySN4eXmYz59Ddbn8G1VV3KGhOCOjEKqK066Tm+3C5SzaP82sEB7p9Sp1sOUSkpOF4nQhhCBP1znvFggUykeEEn1TNOUrOjnyWw6n/wjDkRWFrlvQFIsc7v9FSCGVSPCs1LuFE6fLhsvtQCeUQJ6bLuDsScGOtFz2/HwGl1MYi0KKAiHhGo1Ty1G/mQVL3nlCli7BvGMH2Gz5fmd+i5GRONu0wd2qNTbdzL5fc9n+QzbZZ91+432TWaFOyyiadAjHbFGwuJxYft2K5cc1KNnZoOuEOJ1YhUBYQyG5IfaUdii1QdEUImPdnDslOHkgBGd2KCYRBqpARZPe6WVECqlEUkqEgNwsnYxN2ayecYITh/IKhvAKmC0K9dtEEFPehFW3E5q2DtP8+XDwIEq+RyoAwsNx33wzIr4Sbswc2etk3fxz7N6Yg9NeMP+qagqVa4bSsquKyQyK2wX79mFZMB/Tli2QlwdCoAlBiMWCXj8JW0w02SEhnD5xhu1bz1C5SkUa32riUIZg/88O8jJ1EOEeb1k1GV63JDikkEokpcTlhMN77fy8/AzHDtpwOQpcR1VTqFDZSrNbo6lSXcOy61fUefNg/36UvDyjnGIyoVepgrtzZ1w1a3L6NGxdk8ueTTnYsgu8UUWFyAombupagWpJYZgtYDlxCuvi7zFt24aSlWVM1gpNg/h47O3bk93sJs7aQtiRlsX2Dac5Xd1NzboVaHmbmXLlbexY4+TcMR2hh6PpgKqBUKWYBolcxpNIAlA0BFPh3Ck329Zls39rDi6nv4iWS7DQ4s5y3HhzNKFZmZgWLUL97TcUu73AhKqiV6qE87bbyGvUmHO5Fnauy2b7D2f8RVQBa5hKo46RNOpgxRqhIOx2LD+swbRuHcq5cwUrXooC0dG4W7bE3boNjtAwDuzJYvemPLJOCg4dOMm+jEysVhONUiw0uyOPcomZCC0Lt7Cj6y6E+HvjZa9FypSQ9uzZ00ifd+7cOeP6smXLePTRR+nevTsjRoxg5cqVV7CXfw0zZ84sskqclpbG5MmT/7Y+zJ071/j+P/roo7+t3SuFqqr5CzwKeTnw26Zsdv14FluWv+iFhqsktw6jaadwwqwO1B9+QNu4ESUnB7+l+HLloGNH3KmpOKwR/LHbxtZV5zj1h90v+F41KVRPjqRZpxhi4jRMwk3Ir9vQli2Dk6fAXbAJQVitOJMbkNuhI7a4Spw47CJjfS6nDjnQ3WC35/LnwXPknHMRFmmhXosQWnRxEFszE2HOQhcOdOHyhGpdn3tzLgtlSkjXrl1L69atGTlyJGFhYQDMnz+frl27cuzYMerWrUtaWhrLly+/wj29/Hz66aeMHj3a79qGDRv44osv/rY+3HzzzYwcORKbzca+ffv+tnb/fhQUVQXFM3+ouxUO78nh56VnOXnI7glByscUolK9QQQtbo8lvrKG5dd0TN8vhuPHPTFJ+QirFVeDBtjuvBNHbBwnjrj4ZcU5Dv+Wi9tVYE8zKVSpF0rrHuWolhSGSVUwnzhO6Dfz0fbuQXH4e7giIQE9NRVX/Rs5c9bC9rVu9m7UcWSZUGzhiLwQzpw+RU5ODiaTRmiYmbpNQ2nRWSemmg3dlIUu7J4dXcJNgG0CklJQ5uZI69WrZ6TRO3nyJA899BCvv/46//jHP65sx/4GXn31Vdq2bcstt9xyRdpPTEwkMTGRcuXKXZH2/y4UQM33RIWAzONO1i88y4Ed+YtB+ZjMCpVrhXBztxhqNwjF+ucBTAsXou3aCT7hTsJkQq9VC1f37ug33EBWtomta7LZlZZDrq93qypEVTDRrFMEN7ayEmIVkJODecEC1M2boZCHKyrE4urQEUeLsP7WqwAAIABJREFUFuS6LezenMW21efIznR7ypkcoDlxuT1DeE3T0HWdEKtCzWQFh1vnl+U2MvcLdKfApFrRhFoQMCspNWXKIy3MlClTCAkJ4bnnniu2zOzZs2nSpAkxMTF06tSJJUuWADB16lS6du3K4MGDKV++PM2bN+ejjz6iR48elCtXjg4dOhhTBAcOHEBRFPr27UtCQgL16tXjhRdeMA6/e/bZZ1EUhejoaDp16sTXX39ttF+3bl1mz55N69atKV++PA8//DA2m41169YRGRlJTk6OUXb8+PG0aNGi2Gdp0aIFgwYN4uzZs0XuZWRkkJCQgKIo1K9fn8GDBxvlLuZZAWbMmEFKSgrR0dGkpKQwc+bM0vx1XIMoZJ8RbPg+i21rzpN7zlUwNalCRDkTDdpEktwqFFPeGcTq1aibNnlW0/MRAOXLI269FXfTpthcJnZvOUf6stOcP+X0C52yRqjcmFKOJrfGEh5tQnM5ifz5ZyyrV6OcPYvi6+FGROBo0Zyszl3IiSjP/p0ONv/vPCcP5eUH7SuQFwFOKyazQNMEqqpiMplQFAVzCNRuIGjcNo+ICjbcShYu3YZbOPPnTCUXQ5kW0t9++41mzZphsVgC3k9LS2Po0KGMHj2aLVu2EBsbS79+/cjMzAQ8UwX9+/dn3759tGnThuHDh/Pkk0+yZcsWTCZTkWxQbdu2ZceOHYwdO5Zvv/3WmCdMSUlh2bJl7N69m7p16/LEE0/4ZdGfPXs2M2fOZMGCBaxYsYKJEyeSkpJCQkKCn0jNmzePfv36BXwWRVHo1asXN998M4MGDSpy/4YbbuDpp5/m6NGjTJgwgcWLFzNx4kTjfmmfdfXq1QwdOpSnn36affv2MXToUB577DE2bNhQir+RawuXA/Ztz+Hn709gO+9CMymYzJ5XWJSJ2s2jaNklnnJhAmtaGiGLF0NmJkLTEGYzwmKByEhcrVuTe9ttOCJjOH7IxcaF5zl12IGiYNizRmjUahrOzXdFEpeoobjdmA4cgDlz4MgRUJQCuyEhiPr1Eb16oSVW4cxJhV9/zOPPPXkgvDZVTCaV0FATlWqaiKygoGkamqZhMpkwaSasoQp1GplpcIuL8PJ5uJRsXO68/AUoOcS/GMrc0N6XrKysEnOETpgwgb59+9K5c2cAxo0bR+XKlQ3vq0qVKtx+++0A3HLLLSxYsMD4/NhjjxVJief14Lp27crOnTuZN28eI0aM4J577uH3339n0aJFJCQkcObMGQ4ePEidOnUAuPvuu6lRowY1atQgNTXVyNw/ePBgpk+fziOPPMLx48f58ccf+e9//1viM3/66ac0atSoyGKPxWLhpZdeYtmyZWRkZJCUlMS2bduM+6V91kmTJtG+fXsqV67M9u3biY+Pp1mzZixdupRWrVqV2LdrDbvNzblTdqomhVCzgRU1f4eTokBkrJn6raNIuEGF83ngduOuXx9n3bqgKCgmM6gqhIehd+6MmlgFu1Mj66yT2CpWImPM+bYEigqhURq1mlmpWs+CquHZQXXmLI74SoiOt3psKQpoGkpoKDRrhr1+fZyKhsOeR3QFnSa3RiDy41p1IbCTQ0hMGPFVFFRVRdUK/CbvIlp4lIukVjq6sLNrnUrOCSe6MINQ8oP2y7Sv9bdRpoW0atWqrF+/vtj7+/fvp1atWsbnSpUqUadOHQ4fPnzBeb6wsDBPcopiiI+P59ChQwC88sorjB07ltTUVBo2bAjgN2T3JTw8nNOnTwMwcOBARo4cyf79+1m4cCEdO3YkMTExYD3vGVJRUVFMnTqVO++8k3vvvdeI/9u7dy9du3ZFCEH37t0RQpCbm1viMwZ61oMHD3L8+HHeeust477ZbCY2NrZUtq4lwiI1UrrH0vbucp7vH88edwUFFDCZPOnfRLlyuO+6C71rVxyqv/AoeOY+EWAxCW5sZaXuTeGeoTeA4kZBRwgFTdPR8sVOmEzYm7fA3bRZgTGfuVRNU1EATQiq1A6jUg0rKMLwJJ0ON9lZYZw86cZut2E2m7GYzbgUl2FMCA2BIKYCNLpFYDLnsH2Nm6xjAvQwUM35Ylr209z91ZRpIW3bti1jx45l+/btNGjQoMj9+Ph4P6/M6XRy7NgxEhMTixW60nLkyBGqVq3KgQMHePPNN1m/fj2tWrUiOzubN998s1Q2EhIS6Nq1K1OnTmX16tUMHDiwVPXat2/PkCFDeOedd2jbti0Ao0ePpk6dOixcuBBFURg+fDi///77RT9X9erVadiwIR9//PFF173WUFXPMBlMKCjo+XOHCorf6raiqeiaZ3qp8CSTEALdrXvzkqBqHsE07iMKRBV/j9FsMWEubEsXqKqnvDcLlaZ5hu1eERVCoAAWSwgmzYJiFYSGhaJqmt9/eF0XaEJDqILwaI2klgK37mDr6mxsJwUmPRxUUAQoigzaL4ky7bf36NGDjh070rdvX9atW0dOTg5Lly7lzjvvxG63061bN3788UfS09MB+PzzzzGZTHTq1OmS2vMeerd3714+//xzevbsadzLy19gWLx4MVC8R1qYAQMG8Pnnn7Np0yb69OlT6r68/vrrNG/e3O+aruu4XC6ys7NZs2bNJf2yGDx4MJMnT+aHH34AwGazGe+vKxRPHKmGgupy47bZwOlEcbtBd6O4XCgOh+fldHpW6V0ucDjQc3Nx5+aiO5z54ql6vFLFZzumT3o+RS29QKmFymqaiqoqJS60m00mTGYTJosZ1eTZY28yaVgsZkwmzZg7DY82Ubc51G5qQwvNwSmycOsOT1iUkHGmJVGmPVKAhQsXMmTIEG677Tby8vKoW7cuL730Emazmfvvv5+1a9fStGlTGjVqxMGDB5k6deolh+88/PDD7Ny5E4BevXoxZMgQwsLCGD58OJ06daJhw4bUqlWL1q1bM2XKFFJSUi5os1evXjz11FP06NGDqKioUvfFYrHw5Zdf8tRTTwEwfPhwUlNTqVevHoqi0Lt3b8aNG8fu3bsv6hlvvfVW3nzzTW677TZcLhdxcXE888wzfkdgXw9oqoaqKKjnzmL66SfMp06hxMRAXByiQnnUjAxEZibClodSpYonxMlkQj13Dv74AywWlNtuw16lCrrZZMxJIjBEVFEVY07T931xeMKxBLquoygqqqoaUz6KoqCqBSn/hBC43W5cLieaJtAUDU3VwCQQusDpcHi8TFVF0zxDdwWFchUUGrUVOJ0u9qy34bQJIAJNsaKUbb/rL6VMHTVSsWJFPvzwQ+6///4i93Jzc/n9998DDvGPHj3KkSNHaNCgAVar9aLbPXDgADVr1uTQoUNkZmZSoUKFInOZu3fvJiQkhOrVq2Oz2di9ezeNGzculf3k5GRGjRpF9+7dL7pvhw4dolq1aoDHK965cydJSUmEhoayc+dOqlSpQnR09EXbPX/+PHv37qVJkyaoheb9unXrRq1atfjwww8v2u7lJtijRrzHi7jcNvLcZ8l1/EmtJg66PFCRhAQz5n170TdsgORk9CNHUBx2lJo1UdLWox8/jggLQ6lTB+X33xHZ2Yh69VBtNoiMxNGpE66YaM8cgbctXTe+T0+m/IJ+FO5XSX32imrhsm63x3PMzsrj0KFD/PnnIcLCwkhJaUOlypVwuDzhTS6HZ65U1VQQBQLt1t24XDqnDgu2/aiw92cz9qwwTESgqda/JAWfPGrkCvDbb7+xevVqUlJSMJsLZpDCwsICiihA5cqVL8uZTIqiFCuOdevWNd6HhoaWSkRXr17Nhx9+SFRUFF26dLmkPnlFFMBqtdKsWcHixI033nhJNgGioqL8bIFnamPv3r2cOXPmku2WGQQIRUG3WBChobhcLlRFQdgdcD4LU2goREUhwsPRy5dHy86G3FxMEREQG4sjNBQlLAxFVY3ZVE9uU81P+BRVMQTVO+/qXdASQhhzq754hcxXTL3XVFVF192AE7fbTkxMeUIsFnZu38mmjZuw54flifz0/JqqEhoWQVhYOKFhFkJDrYSHhRMWHUpyihmTWWfvZhO2M3ngBjQLKmY5X1qIMiWkt9xyC+vXr2f9+vXMmTPnkjytS8FqtXLbbbcREhJyWe3+/PPP3H777Tz44IN+vxSuVjZu3MiECRMIDQ31i4a4NhHoioIrLg6lYUMUWy6iRg2EzYZqteKuWBFht+PSdURMDHr16mgtWqBHRCCEwBViQZhNATdc+gqhgeoZWgMIXRS5V5phv+enp25oaBjVqtXAkadjt9vIyjqDzWYjz2eLqdvlxmbLNRbRwDNlVKFcBcqXr0hkRDSJyVG43C72/6KTdxaEW2DSQMUkQ6N8KFNDe4mkMH/Z0H5ALPFVrR6Pkfx5SB8v0fve+/ImOPEOkY15UB+vsaQ++Hmk+VEBwifXqYKSPzd6YXu6rmPPc5GTnYMQCi6XG4fdQU7ueVxOhyHubreLXFsuutuN2+3G7rAjhCfczRoSgtlsISI8GlWP5fD2SA7+GoX9fASqsKJpVjTl8oipHNpLJNcBnjPji3qR3vfeGFyvwHnnQHV0o1xJoqooit8QXlEUwytVVAXdraNoCopQjFjWksRUURRUTcESYs5fxArBGmolPMKKw+7Cq89C13G6bAhdR9d1nC4XIv/APmf+MSaqphAW4eSGZnY081kO/eomJxMUXUNRkcP8fKSQSiRB4iskutANj9IrwIboCVAV1W8oXZwdQ1wpCI/yrvr7inqgTSOeFXzPir6qqISEmvPLmXHYC/IFeLxxq+doaAG6W8fl9Fx3ud0IXcdstmC2aGjRCmGRbixh2WSs07CfMaEKDaEIY0riekYKqUQSJF5h8w7/oUAwvR6kb9yo955XGAOlrvO1aYRM4ZlC8Hqvhqes+M+hKoqKy+Hi5IlMomPCCA8PRdc9may8O6d0XaAoIITFaMflciN0j4ednZWFGzdh4RY0k6eOtbxGlSQL+7fbyTltQ1PMKEJFSK9UCqlEUhTFiLFU1YK5UD9xK7m64RXqwmdXk6IaoucbmO8VxeIEVcnf4+9d8fedrzU8V+OoaM+uJm8cqbdM4WiBAiEtqKdpHi/W5XTjdDpwOp1ophhCQjwerapphFh1dJGD061iUqyoikWen0cZE9JRo0bhcrkwmUyMGDECgO3bt7N06VKqVatG+/btqVix4hXuZVFWrFhxwd1UixcvxuFwcPfddxe5t3PnTpYvX87QoUP/qi4Cnm2mTqcTVVWLZL66nvDuj1cVFfK9wOKG0H6hTIpSZHiu5e9T9y5YFbRR4KWqimpMBfgG7Jemo35B/vk2BGAyq0RHRxIaWhA37ds3vyOmKcjlkP8BTVNx6/mJThTQTJ4NCmbNiaK4PNMBBWei+nxz1ydlKn7hzTffZM+ePcbnCRMm0KxZM+bMmcO4ceP8kjuPHz/eb5/95WbXrl188MEHFyx39OhRevXqZeQuLY4lS5awcOHCYtsaP378JfXzYtm3bx+vvfba39LWVYv3wCalYOHIuyoPgRecLjS09YqsERvq44l6V+W9bXu9yEA2vZ6uonp+IiiIJMi/hyIwmVViykeVegOKYVdRUDWN6HLRxMZWICTEgqZp/nO2fjUFunBTGt2/lilTQgrQt29fwxt95513GD9+PGlpafz444+MGzfOKDd8+HAjy9JfwcKFC/n+++8vWG7y5MmcP3+euXPn/mV9uVz84x//oH///le6G1cNXsHwippXbLwv7z0opQeJZ3jvffmJsCFSip/oqopa8mKO4rNX31j1VxG6IDc7F5fbdVHzl562QVVMKPkDVu9efiVfwItynasoZVBIfXE4HH6eXkREBADPP/88eXl5vPTSS6SmpnLy5ElsNhupqalMmzaN+vXr07p1a8CzxfLuu+8mOjqaFi1aGBn0wZOwY+jQocTFxVG3bl3effddANLT05k8eTLp6emkpqby/vvvB+yfEILPP/+cgQMHFskyr+s6zz33HBUrVqR9+/ZF0gFOnTqV2rVrU79+faZMmWJcX79+PX379mXcuHHEx8f7JWRu3bo1MTExdOnShf379wNw4sQJBg8eTEJCAu3bt2fFihW43W5effVV6tatS6NGjf42b7es4+sl+oqdr8iWJFq+guwtr2laEaFUFMUQWoEoMamJn8DnJ0fxJIzWiI6JwhoaWqRN38/F4XI5cLrseEXS+8vj+h28l0yZmiMtzEMPPcQ//vEP3G43Tz31lHEg3gMPPMAnn3zCgAEDaNSoEVFRUTidTpYvX05OTg5LlixB0zTsdjt33XUXgwcPZuLEiSxZsoR77rmHtWvX0qhRI4YMGYKu66xfv57jx4/zwAMPEBUVRb9+/bj11lvZvHkzI0eOLHb76dKlSwkNDeWdd96hatWqHDlyhCpVqgDw9ttvs2bNGhYtWkSlSpXo3bu3UW/VqlUMHz6cTz/9lPbt2/PCCy8YyUcyMzNZsGABoaGh7NixA/Bsmx08eDBjx46lbdu2jBkzhtTUVLZt28akSZPYsGEDmzdvZvPmzURFRbF+/XreeOMNNm/ejMlkuujEJpKC4bcRM1poDvViPFVFUVDxzJN65x29YVSKohgr/L6p/C5gEZfLTXa2jRCrmbDw0IIIAW88q9F20V1TiqIQHhFmeMTGc1zv4/cSKNMe6RtvvMGUKVOYN28eNWrU4JtvvgEwEm00atSIDh06+G3tfPnll6levTqJiYnMmjULXddp2rQpO3bsIDExkfbt2/PFF19w6NAhJk2axL333ssff/yBw+GgX79+TJo0iZiYGGrUqEF0dDQdOnSgXr16Afs3adIk+vXrR6VKlWjXrp2R/T4rK4u3336bzz77jJtvvpnq1av7HWj35ptvMnz4cHr27En58uXp2rVrEdsffPABsbGxxMbG8v7779OmTRvKlSvH9u3b6dKlC3l5eSxZsoT4+HgOHz5MRkYG3bt356abbiI+Ph5VVVm2bBmNGjXinnvuuZx/LdcEineeNOA9f+/Td797oAUn35/F4U0crSoqmqr5iah3KgAwPNWSPV9wOnSOHzvB8WPHsefZsec5OX8uC7fbzbmz5zj651GOHjnK+bPncTpd5ObYseXYsOfZOXvmLIf/OMLJE6dw6+6iz4vA6bbLs518KNMeKcD999/Pfffdx8iRIxkwYADHjh0jPDy82PI33HCD8X7Xrl2cOnXKLxs8eLJM7dy5E5PJVGTY7j0+5EKcOnWK+fPnG0co9+zZk/HjxzNixAi2bduGzWYLmGRFCMHatWt58cUXi7UdFxfnlwow0HMkJSVhsVgYNGgQe/bsoWfPntx7772MGTOGOnXqMH36dEaMGGGc7VTa57pe8AhW0ZX5kggkbr47mkqyVTjDlhFQn+/reL1SY5XfJ2O/0Y5PCJU11ExcfAUcDkd+PY+XqigKTqeLPJvdszLvdhvbT0FHURUcDk/kRmhoKJrJ5LMwBihgCXHhFudx6jFYiJTDfa4BIQXPP+AhQ4YwatQodu/eTdOmTYGiw63CxMTEkJiYyLJly4rc27BhAy6Xi6+//pry5csHrF/Sf7CJEyficrmKJPfYsGGDMQWxefPmIjlLFUXBarWyZcsW46ypCxETE8NNN93E2LFjA94fNWoUzz//PL169eLhhx9m7ty59O/fnz59+jBs2DBSUlI4evSokZdSQv7Kur9nWZwYFvYQi1t1D1SmuH9DBXlHFWPoD/gN/zVV89uT773udsPpY3DmeCQR5VV2bzWjWARRFaKx2VV0vTyKGk3eWR37OciNUnHYQgiNUHE4BMIch2oCm0PFcVIhMgLCwwpCnSwWHVUDgQO50OShTA/t161bR0ZGBidPnuStt97CYrFQvXp1wHNkxi+//FJi/a5du7Jt2zYmTJgAgNvtZtWqVYBneiApKYlnn30WV/4Z5StWrDDqVqlShYyMDM6fP1/ErhCCyZMn88477/gltkhNTWXGjBnUr1+fmjVrMmbMGNavX48Qwsi+D3DnnXfy5ZdfsnjxYux2O0eOHCnxOXr27MmkSZP46aefADhz5gxbtmwBPOdW7d69m9jYWFJSUsjMzMTlcrFixQrMZjNdu3bl9OnTOJ3OEtu4XlAUBYvFjOZdXClhuF4chev5zqX61i+cAq8kO0Zoks+KvxHHqfjaUDl3UuG3dIVTJ838vsvEvh0uMn4SpH1nZu03GiunWtj4vZWfVoayflEoP8wN4eclZratUflxloVN8xX2bHSybaUgY4OKPc8nqsAn+N+DFFIo40I6fvx4kpKSiIuLY/78+Xz55ZeG9/jvf/+bESNGoCgKr7/+esD6ycnJzJ07l5dffhlFUahQoYKxgm21Wpk/fz6//PILZrMZs9nM66+/bhx417t3b6pWrUp0dDSapvkdv7xixQr27NlTJJTo7rvv5quvvkLTNKZPn05aWhpt2rQhKirKLz72ww8/JCwsjDvvvBOr1cpXX31V4vfw0EMP8dxzz9GyZUsURaF58+ZGaNYvv/xC8+bNURSFL7/8krfffpv9+/czePBgFMWTSf+zzz67pITX1ypq/omdCgXhToXnCQuv4HsJJLzekZGfIPqIZCAPtriVdb/52PwAfiOkSvWESgldwe0EVdNxu3V0N7hd4MhTcNkVnDYIC3dgjdApn+BC1wVZ5zROHDPhdCnoLhWTSSMkVKF8nEZMtGoE/ZvMCgUDF28QvxTTMpVGLzw8nFmzZvktvvzxxx9kZmbSoEGDIkPTI0eOsHfvXpo2bVriMR55eXls376dxo0bF8kLqus6W7du5YYbbiiS/9TtdrNp0ybCw8Np1KjRRT+P3W7n119/JTk52RjuF243MTGx1Lu1zpw5w6FDh4oklbbZbGRkZJCcnIzFYjH6vm3bNmrWrOn3XEuWLKFbt25+vxiuZi53Gj2b8yjJrRQ6D6hIfKIVb5Y4322ivu99RdJbrrB9L4Xr+m/ZLNlOIFu+/fIuVAE47Dp/HnRy6qgbS4TGudNmrOGCqAqCUKuCzSZQVIFmAqtJ53ymgi4UXHk6wg0mK5gjPRmkoqJVysXonoUlBc6fyWPlzLPs2KASolbGaorFrIWhqRYuNaXetZBGr8wJae/evUlKSjKC8iWXj9GjR5ORkcGMGTOkkAYQ0sL1fIWx8LULtVm4nN8WzUJlS/MM4LNFND+bkxDea56triiFbAnyBVItyOInCiIIwLsfvyBP6tnM3EJCWgGzFn7dC2mZWmzq0KEDR48e/Ut3LF3PpKWlYbPZuPXWW690V646Ci8Uea8V9kq9730PpivOVuGhfOHFUd+6xe3399b1zRLluegRQQUgP0zJSFDtM53gQS8YnCsU1KOQdyyX54ulTAnpd999d6W7cE3z7bffXukuXDUY6eoCEGg+0zfLkq7rRYbhpfFWA5XzCnLhMoHeFxezagg9CkIRRk5T3zOiSupXwWF9IDADGnJe1J8yJaQSyd+D4tlfjsc7U1UVt7vkUDpfIfMVP1+RKq2YFuexeu0WnlK44NP42vEeY+KzwFU4RWBJfVQULX/pXgqpL2V61V4iCRaBfkFB8ubuLC7qqbjV9cKeZXECGciO7/vCQlx41f9CFI4AMH4SOGKgcL+Ma4DZ5EZTpYgWRnqkkusa5YIHZXiG6263O19cClbri/PcvEN8oEj8aHGr/8V7gIFteRFC+HnAJf1S8Ab5+2Wvyv9jrPorRSMIfNtRVaXoopVECqnk+kYYex8LoxiZlBQj4N0rVrpHUPDMGxYIjiiS16Oklf1AwugtE7BHAYb0vl6mr83iFqZ80//5JiXx9U6NzP7+30ZAexIPUkglkgB45kc94uFyudH1AiEs7EV6QoQw7l8IrzdbeFHKy8XMfxbuE+g+Yu7NJOU9fsR3zvYi7OPN5C/FtDjKlJAGOmoE4Oeff2bjxo3ouk6LFi246aabjDNqfvjhB5o3b05kZORl7cuxY8eYP38+VquVVq1akZSUdFntXy727dvHH3/8UeR6q1atsFqtHDhwgKysLBo2bFiinbS0NGOLbPv27WnXrt1f0t+/G+/RHAHvKQoul4usLBt2u4oQhYTkktZcfIQYEbB+oLObSkJ3u3E5HSB0IxbUOLXEiCUt3VHOxaEoEBVlRVWVfEGVw3tfypSQvvnmm0ZAvpdp06YxcOBA6tSpg8lkYteuXaxfv55WrVpx4sQJOnbsyOLFi+ncuTNLliwhJyeHXr16BdWPTZs20blzZ6pVq0bFihUZM2YMO3fuBLjoNi5Xn4pj+fLlzJkzx/i8Y8cOKlSoQHp6OgBjx45ly5Yt/Pjjj6WyN2XKFEwm0zUjpKI42VKU/CxLCtnZKi49mqvzv4tA1wV2ew65OefR/4L9NQoCi9lBWFj+yafSMS3C1fgvo0T69u1rbBHdsWMHDz30EFOmTGHQoEGAJ0lHzZo1AYiPj+fnn3+mSZMmgGdvfsuWLYMWrY8//piuXbsybdo0AL8s/RfbxuXqU3E89thjPPbYY0Y/b7jhBkaOHGksYPzrX/8iKyvrgnbatGlDmzZtAmbKKssoKCiFPc18BAKXS0cXKhACytX530VR3AjFgVuYLpjx7JLsIxBC9X7gEl3xa5oyHf709ttv06lTJ0NEAUNEATp37szw4cM5evQoU6dOZdOmTUybNo3U1FSWL1/O5MmTefnll/1sPvfcc34eXCBcLlfAI04CtQHwySef0LRpU2JjY+nZsye//fZbieWLO+IkWD766CMqV65Mv379AJg+fTr9+vUzjkH5/vvvuf/+++nfvz/lypWjffv2RhapaxXvVsjiKAvTggIXQs+76CmBS6MMfCFXgDItpFu3biU1NbXY+0OHDmX58uXYbDbat29PnTp1aNeuHSNHjqRhw4a0aNGC999/n7NnzwKepB/jx4+nVatWJbb7wAMPsGjRIh588EGOHTtmXA/UBoDJZKJ///589NFH7N+/n4ceeqjE8kOGDCE7O5v169fzxRdf8NkSTGNWAAAgAElEQVRnnxmp/i6V7OxsPvjgA78TQtu2bUtsbKwh7MeOHWPp0qU88cQTbN26lfr163PHHXdw4sSJoNq+uil5jjTQmUpXG4piRlHD/pZ+FsyRSnwp00J65MgR4uLiir3fpk0b432NGjUoV64c1atXp0OHDsTHx9OoUSOaNm1qeGSzZs0iJSWFxMTEEtvt3LkzP/74I9nZ2VSrVo1///vfCCECtgHwf//3f7zwwgs0btyYvn37snnzZnRdD1i+pCNOguGjjz6iRo0a9OjRw7hWvXp1EhIS/MrVrFmTdu3aUa1aNT755BMsFguLFi0Kqu2rmSLpNQvdu/oRuJxOHHbbJS0iSS4PV+ekTympXLmyn0d4KTz44INMnTqVJ554gjlz5hjD3gvRqlUr5s6dy/fff0/Xrl1p165dsck+Xn75ZSZNmkRKSgqqquJwOMjNzTWmBHwJ9oiTQHi9Ue+ZUaVF0zRq1qzJ0aNHL7ntq51Ai03ebaG+43pFAZMJI6uSqnp+6gI0FbxTk6rqyVDvre7WPe/xKWvUU7z71/Pt6QXvdd0bvwouN5hUIN+eN9zKE9KkYDZraJqJv2zusvBvlLLxG+ZvpUwLaZMmTZg7d26J5xsVpvBk/IABA3jhhRdYs2YN69atY9asWRfVhzvvvJMGDRqwbds2Q0h92zhw4ABvvfUWv/76Kw0bNmT79u3Mnj272D7FxMRc8IiTi+Wjjz6iXr16pT66xEtOTg6//PKL3xz0tYbnkLkA1/MD7jUNwiN0zBbPoXTgn2QJ/ONIfd8XLiso3TZ1Pxs+oulrz/gpBC6XGU21YDErRTYEXBa808iK55drWLSGKUSAPFTBoEwP7d966y12797Ngw8+yP79+zl27BivvPJKsfOJVapUIT093U+4wsPD6dOnDwMHDuSuu+6iXLly/PjjjwwcOJDMzMyAdnbu3MmGDRvIy8tj4sSJbN++ndq1axfbBkDu/7N35/E13fnjx1/nblmRSAiNJUEIIihRW4i2lqgu2qG6UdWZX01b35ZBtWpaUnRjOkynnRZFpx07pfZqat/XqFCpJZZYIojs997P748rR24WolfI5f18PNIm53zO5/M5V7x9zuec83lnZgKOFM3gCFLFlb9RipNTp07xwgsvsH379lJ9Runp6UycOJHu3bsTHx+vf6WlpRVbPj/lyOHDhxk9ejRKqZsOwO4kfwGPwgq8HInJBB4WDcvVr4LfWywaZnPx3xcu62HRsBTaX9yXUx3mYuoo+H8PA56eGt7ehmv13+qvq20ZDWDQNExmjRJeyrpnufXHERoayqZNm0hKSqJOnTrUrFmT3bt3Ex0dXWz5N954g02bNmE0Gp1uUj3//PMcO3ZMv6xPT09n1qxZ+k2YwtasWUN0dDReXl4MGjSIt99+W38kq3AbISEhDB48mOjoaJo2bcrBgwcJCQnhm2++Kbb8jVKc5OXl8csvvzgF1+v597//zblz53j77bfp1KmT/lVSPquDBw+iaRphYWFMmjSJf/3rXwQHB5eqLXeUn/bYndntdqxWa9nNkSrHdIVdpmBL5NaX9gANGjRg/fr1HDt2jAoVKjhdDvv5+Tn9coWHh5OUlMT27dudsntWq1aNgIAAHn30UQC6du1KWFiY/vxpYa+99hr9+vXT03cUTBNSXBuffvopL730En5+fgQHB5OamqqPdosr36BBA/bu3VtsipPatWvTrl072rdvX6rPZ9iwYQwbNqzE/YXnYiMiIoiPjychIYEmTZrg5eVVqnbclSrm/fh8mmZwiyBrMNgxm6xoWsnn4hLNMcVh0O/Mlf/P5HZzu0A6a9Ys9u7dWyTVSH720Bvx8fGhY8eO+s9xcXHMmjWLIUOG6Pma3nrrLeLi4q4bRCpUqEBUVFSp2gBHor18AQEBBAQEXLe8wWDQ00oXNGfOHOrWrVskjfOt5O3tTatWrZy25b8iWjDb6d2gpNWfHEvL3fbu/CF2uwmrzQOlMgHXUq8USzlufjlusrnHPy63m1sF0rJINZKSksL48eOdEuqNHDmySKK78qJLly706tWrTOquVq0a999/f7H7Tpw4wdq1a6lbty41a9Ysk/bvhCIDuKujLlWoUH4epPLmWrrvsutj/pMKomRulfxOiMJcTX5nVzbsdhs2ezbZtotkW0/TLMbCYy8GUzXYg6ysLFJTM0ALAsw3rO9OsNoU6ekZXElPw24vgxEpCosph6CqHvj4eBG/8CzrF2ej5QRJ8rur3GpEKsStphW+uNdAM2l69lC7XSMr21Sub7QoBVYr5FmvPc96K2k4nmM1GLSrwVIu7QuTQCruaQrlSAh33UKaYwm9cho/7MqGUlbKfCGR66Rbude59eNPQrjqeqlGNE1DK/cPTCocN5hykRWZ7pzy/lsiRJkqbsUkDdAMBjSDwbHcYLkehmlomhk0L8p+yHw1/YpEjSLk0l6IYhTMa6+hQFPl8srescq+FVQWt2NEKo8/Fc9tAmlqair79u3DbDYTGhrqtGrRggUL8Pb2pmvXrnewh8IdOZK+lbzfoCksFhumwi/YlxdKw2YzkJNjxmDIpiyCqca1RVjK54dw57lNIN28eTOPPfYYMTExbN++ncDAQN555x1eeuklFi1aREBAgARScdNuFHY0A3h4apjNBgyG8veYjuOOvQaYMWgayl42gc5o1CiHp19uuE0gBfD09OSnn37CarUydepUBgwYQJ06de50t8RdzmjQMJkMegrm8kbTHIuX5OaU0epPFByRiuK45bSxyWTiz3/+M1WrVuXAgQOAI1tmTEwMlStXpm/fvly+fBmA3Nxchg0bRnBwMLVq1WLIkCH6qkrjxo3j+++/JzY2Fn9/f3r37u301lR8fDxt2rTBz8+P2NhYjhw5ou/r1q0b3bt3v41nLcrCjWKDe9y5t6FULtqNHuO6BRzPkkpELay8/4aUaPv27Zw9e1ZPC5KUlMTEiROJj49nz549ep6jt956i8WLF7N8+XK+/PJLfvjhB95++23AsdLR2LFjGTduHOvXrycpKUk/7uDBg/Tv359hw4Zx+PBhIiMj6dy5M1lZWQDYbDZstrJ4i0TcTiVmES3AXeJGWb6jaLM6Xk4wGAyYzWYMcuveiVt9GtnZ2XTu3JkmTZoQExPD+PHj9YU9unTpQvPmzYmMjOTpp58mISEBgK+//pr33nuPJk2aEBsbyyuvvKJn/8w/rlmzZjRu3JiePXvqI9xPP/2Utm3b4u/vT0JCArGxsWRnZ7NixQoAVq1apX8v3FdJCzu7F6Mjb1MZnoeehTk/j5Wh/L6gcCe41Ryp0WikQ4cOBAUF0bZtWyIiIoot5+npSUZGBpcvXyY9Pd1pkY0WLVqQkpKiX94X5O3trY84Dxw4wPnz5/nggw/0/Q0bNsRisdzisxJ3UkkLOxcogM1mw2CwUh4jh1IKm82O1Wq7eh5l8I6oUtjt1xZEKX+fwp3nVoHUbDbz7rvvlrp8xYoVqVixIgkJCXoivFOnThEcHIzJdP1T9/PzIyoqigkTJrjUZ1G+XW9hZw0NzWjAaICc7DNomuYIVldzgTh+dpS1q0IBrNjYXDbX3o5UJFYslrwi/ygUToHilKak9C2goWGxGCWDaAncKpD+ET179mTOnDk89dRTVKhQgWnTpjll0rzecW+++SbPPPMMUVFRpKWlcfToUX0qYfTo0RgMBkaOHFnWpyDKUIkLO2saRrMJk9mEJchcdCb1atK64g52jBJtGI1GbDYbmqY5UsmUchKz2NQnV4N4waB/o4Xb8kermqZhMGhYrTYMBgN2ux2DwYDNZqe04bRw28LZXR9I4+Li6NmzJ+Hh4ZhMJurWrcuoUaNueNxLL71EcnKyvsBxaGgoAwYM0APpypUrMRqNEkjdXInv2iuw2+wYTQYMJqM+P4gqEFKvrv9poNB8oQK72ZHu02Az6qPXYicxlfPUgmNBae3aPhR2ux3NYMCaZ73Wjxuw2+2oqwFTMxgcN4kKzUoppVB2e5H2DUZDfvP6PpvNhs1qk9f5S3BPrEdqt9tJTEzEZDIRFhZ2U/+ypqWlcfz4cZo2beq0/cqVK2iaho+Pz63urrgJrq9HakfZrVjz1yO1neb+hzzo+XJtqtf0Ac0RUEHpo0JldwS/zEw7p45mkpVmKxBcHUHH7G2gTkNfvHxN2K5cQR09CmlpkL9e6NUgZfPxQQsJxSMwgJxsK2dP5nDprP3qaBdsyjH3aTBqVA+tSEV/hcFgh+xsOHESw4VU5zrtdvDwgJo1UVWrohSknrGSmpJHXk7B6QeFwk7lqhaCanhiMgN5eajTpzGcPo3KznYEWdvVY4xG7MHBaDVD+PnHNFb9Lw17ViCeRlmPFO6BESk43g9u1KjRHzrW398ff3//ItuLy0kv3FExjz9pGkaDCa6mGzGaHH/R80d5Co2MK7AjPo0lUw6SejLbaaRmMEGbR6sTXKcePuZsshYvIW/2bNSpU04Lhtq9vVGdOmF56SXIsrJvyzmWTk/m+IFM5/qMGiFNKtB7UH38ArwxWK3k7thBzldfox05gpb/GJ5SKKMRLSICj/79MQYGceRQBstnHOfXzWnY8pzPtPJ9Zh57JYQq93k4RtFHjpA7Yyb2jRshI+PaVITRCHXqoPXvj6lm6C363O8u90QgFaIkxV3aa2gYtPw9hS97jVhzFScOX+SnWYc5sCHNaaRnNGnUjvCleftAKlY0krVnL3nTp2PfuRNycq41YrGgWrbE3L4d3tWqcfJkDhuWn2PnqgvkZNgKtAlVangS/Vhlgut4YTYbsB0/hXXefFi/Hi5fvtZDoxHq1sXcsiWm8HAuXcpm3fxkNi5M4XKqcxJ674omWnarSmiDSpgtBlRaKjmrVqGWLoXTp68FfE1D+ftjbNwYU8OGaGYzZg8jRpNWFs8HuC23eo5UiFutNA/k62WVwma1k3Iik/glySSsvUBe7rVwYjBoBFT3oMeAYBq28EO7mErujBnYExMhN/daPUYjhITg2fMJfFq0JCPTzq6fTrFz2TlyM52DqIePkQ69gmnSNhizRcN+6RKZq1djW7cOLSODgoWVvz+Gdu0wxsSQa/Zh7+bL7PoljfQ05yBq9tBoHO3Hg09Wo3JVC+TlkLVjJ/bFS+DcOacgipcXxnbtsMTGogUGOuZMr6UTFVdJIBX3tJt7IF8jPS2XbSuSWf+/U2RetuoDVk1zjPIe6FGN1l1C8FI5ZM6ahS0+Hi5dcn4OqXJlDN26YenxKHmeFdm/LY34eadJPZ1z7ca+BhZPA02iq/BQr5pUqGjGYLOSuWMntgULISXFkWw+v04fH4wPPIDnk09iqB3Cbwcz+Gn2KY7/mkHBJ7OMJo3ajSvQrX8Nqof6YMSGfe9etO++Q/vtN+eAbzZDRATGp55CCw93jHjlOfxiSSAV97QbPpB/rSC52XYO7brIukVnuZCSowcoTQNPHyMN2wTw4J9qE1DFSOa6deT9b1aReVG8vNDub475ySfQAgJJSkxn9ZxTJO264jSH6Qh4Fek+IIz76lXEaHTMYarv/ouWmIhWMOBZLBARgbl3LwzN7+fsmVw2LT7DwS3pWAuMmDUDVL7Pgy4v1iK8aQAengZsp06Rs+gH1KZNjnlRvQNGtOBgTLGxmFu1QvPyxGgyXl3o+g9/3HctCaTinna9B/ILystTHD+UyZp5pzi885JT0DNZDIQ0rkiX50Jp2DwQ29Ej5H7/PSQloRV4g055eEDjxnj27Yt3eCNSz9nY8mMK+35OIy/7Wn0Go0blahY6PlWdiAd88PAA66VLZHz3PbZ16yE9/doI12BAq14dc5cuWB54gEyrkd3rLrBj5Rky07KvnacGFfzNtH+yKu1jq+FTwYCWcQXrunWwbp1znZoGgYEYOz+MqWsX8Pcr0DfjH747fzeTT0SIG+W+U5B+wcrmFWfYE59KdoF5TKNJI+A+T9o8Wp2WHQMwZF7gyrz52DZthqws54BXrRrGxx7DIzqaTLuZfZvPsmNVCldSs/RRsWYAXz8TzR8OIKpLEL4VTZCbQ87qn1ArV6JduOA8h+nnhzE6Go9u3bBXqszv+y+xcfFJTidlYC+Q+tTsaSC8tT8degbjW8mM2aCRs3s39vnz4ehRRxrS/PP19kZr1QpTj0cxhoQ41tC7SmZHiyeBVIhCCg5QNTTysiBh20XWLzzOxZRMlP1a0KtQ2UKLh6sT3aM2Fb2sZKxehXXRIkhNdR7h+ftj7PwwPo/1wO7ly9EDl9jww2lOJWVgt117id3Dy0jjtpXp0ieU4DoV0Ox2ruzchXXKFDh27Nq8KDimCZo1w/Lkk2ihdUg+ms36RSkc2n4Zu7XAiNmsUbOhDw8/E0ytuhUxGBS2U6ewzp+Pff9+xzOp+SwWDA0bYnnsUcxNIx3zouKG3Obxp/xUI4U1a9YMPz+/Yo64eUopfvnlF1q0aIGHhwcffvghAwcOJDAw8JbUL8qfwoNRDTCbtWvrbiqNsymX2Rl/EmuelYBgD72syWKgTpNKPNirBjXreJN9cD85P/+MlpkJwcHXKjWbMTRriufzz2OuXZu0NDt7N13g3IkcKgV6UDHA0QuDUaNqLR9ietWkwf2VsViMZJ6/SN7P8XD5MlSt6hycg4MxP/UklvubczkH9m09z2+7L+NVwYyXr/nqYswavpVNdOxdjYg2AVg8NYxKkb12HdrvR8DPDypU0OvUqlTB+MTjmNq1A29vlN2uT33cA+/u/GFuE0jzU408+OCDTts//vhjmjVrdkvaOHv2LJ06dWLZsmW0b9+eUaNG0atXLwmkd7WioTT/itigaShlAM1C0/ZVaBTl71TcaNIIquFN/ab+GM1g8PLCs2MMhqhWUHCVMLMZY1g9PBo1wuDhidmcQ70IfwKr+2AscK1sMGr4V/WlTkRFvH2NaAosSqFFRUHduk43rZTBAFWrYolsgtHPD+NlG7Xq+/PIAC/sNoXJpGEyO55I8KhoIrShL5X8PTAYNVROLh4hIVheftnpkh5AVaqEIbwBxipVUEbj1SSAmuMhMbtCMxgc9cq1rBO3CaTgWB5v1apVZVZ/UFAQO3bsoFmzZmRmZpZZO6L80DCgtAIBCrDm2rmYlofBYgdlx9NL0ahVpWvLJ+nHOvIYZWTmkJEJyjcQors4anGaH9DAaCQrww6ZmdhtirqRFfQpgmt1OkbCSuWSluqoRmne2CJaoOzFvAprMJJlN8KFDOx2qFnXTI0Qs2N1J+1a046yeVxIzb/TrzA1aAL1i1lwBbBpGlzMpPBsqN1ux6BZyMnMk3fuC3GrQFqSpUuXkpiYyKFDh5g1axYRERGMGDGCjz76iISEBLp3786///1vfHx8WLNmDaNHj2bXrl1ERkbyzjvv0K1bN8CRPsRms/HNN99QqVKlIu3ExcUxdepUNm3aRFBQ0O0+TVFmNDTNiMngicnuw5GEDBZNSULzvEQe6ajCS+TdRgqwWfPgFvShYOy73g2jYhfDAoyaFx5U4uIZD3IzPdA04w1qune4VSDNy8tjzJgx+s/Dhw/HYrGQkpLCiBEj+OSTT1i3bh0vv/wyAwYMYPHixRiNRnr27MnkyZMZPnw4ZrOZNm3a8H//93/MnDmTXr16cfToUQICAhg0aBCPPPIIWVlZxQZSpRTp6el4eXndztMWZUwDDJoJo+aByeDL+ZMZpBy9SG7eBbK0NBR3MqVMeRn6aVjsVjxNFkxmE14e3vpiJY7r/Hs7oLpVIL2epk2b8vrrrwPQq1cvli9fTsuWLQF49NFH2bt3LwDR0dFER0eTnJyMh4cHCxYs4MCBA7Rv315f/Lkk77777k0tLC3cg6YZ0FBoGDAbvPG0BGI1eAMWDHZ/1G1IKlfeaYBmMOLh4Y/F7IPFUAGj5oFBM2PQ5M6+WwXS0q6QX/gB68qVK5OSkgLAwoULGTRoEPXq1aNx48ZYLBYuXbpUJv0V5V/+KvcGzYjJ6AE2hafRSJ7BjNnsnb96870+4AK0q1O/BkwGb4yaB0bNgtFglgf0cbNAeisMGzaMoUOH6qPXuXPn3uEeifJAKYWGCZPRC7uyYbCbUIXWN8pf3kS7+t8bLXeilapMft3Xd2fqKu5fDw2jIX8UKpf0+dwqkNrtduLj45225V++34zsqw8gb9myhQsXLpBR8B3jq3x9fWnUqBF79uwhPDwcgPnz5/PDDz/wr3/9SxZ0vos4VqV3XJ4qpV29G3/tctVxs8l5nShHMLJTXCDRrpbWMKCwXy179Zj8nE9F6lLF1OHYZ8eOASMK2w3rKi4b043qcpwkxfTLUGhBF+1qLUbyF7qW9CMObhVIs7Oz6dSpk9O2Xbt23VQdY8eO5dlnn+Xbb7+lUqVKdO3alRkzZtC7d+8iZceMGcOQIUPw9fXlkUceYdu2bSxZsoQPP/xQAuldyhEcrv21UEoVGJkWWPqoQBI85+1X/6NnmytYRl0LgDdRl0Gvy3zjuvJT291kXfmX7vmnqferyGcjl/HFuSdSjRR26tQp0tPTqV+/PlarlT179pQ4srVarWRkZFCpUiWUUpw9e1YefSpHXE01Iu68uyHVyD0ZSIUQ4laScboQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrhIAqkQQrjIdKc7UFqpqans27cPAH9/f5o0aYLBcO3fgd9++42cnBwiIiJITU1l//79dOjQoUg9SUlJZGRkEBkZWWJbBw4c4MyZM8XuCwwMJCIigpMnT7Jq1SrOnz9PgwYN6Nq1KxaLxcWzFEK4I7cJpJs3b+axxx7jwQcfJC0tjd9//524uDj++te/AvD5559z8uRJZs+ezebNm+nduzcZGRlF6vnPf/7Dr7/+yuLFi0tsa+HChaxZswaA/fv3Y7FYCAsLA6Bt27ZUqlSJmJgY0tPTqVOnDjt27KBVq1YsWLCAqlWrlsHZCyHKM7cJpACenp6sWrUKgH/84x+89957eiC9lUaMGMGIESMAeOaZZwgICGDy5Mn6/tjYWCIjI5kzZw4mk4mDBw/SuXNnRo8e7VROCHFvcNs5UpvNhqen521vd8+ePSxfvpy4uDhMJse/Qw0aNOC9995jypQp5ObmAtCtWze6d+9+2/snhLj93GpEmpeXx5gxY9i1axc7d+5k2bJlt70Pu3fvJigoiMaNGzttb926NdnZ2Rw9epT69etjs9lQSt32/gkhbj+3CqQAderUITg4mLy8PPr3788vv/yCl5fXbWv/4sWL+Pn5FdleqVIlANLT0wH0KQghxN3PrS7tzWYzzz33HC+99BKLFy8mOzubb7755rb2ITg4mJMnTxYZbR47dgyAWrVq3db+CCHuPLcKpIVpmkZKSsptbbNly5ZkZmby008/OW1fuHAhbdu2pUqVKre1P0KIO8/tLu0BDh8+zJw5c0hISGDGjBnFlrHb7cTHxztti4qKAiAtLc1pn9lspl27dqVqOyQkhP/3//4fr776KjNnzqR+/frMmTOHzz77jHnz5unlRo8ejcFgYOTIkTd3ckIIt+NWgTQzMxNN0/D19aVVq1YsWrSIpk2bFls2OzubTp06OW3Lf6B/w4YNTvuqVavG6dOnS92PSZMmMXToUDp16kRmZib16tVj7ty59OjRQy+zcuVKjEajBFIh7gGaklvLf1hGRgbHjh2jUaNGRfZduXIFTdPw8fG5Az0TQtxOEkiFEMJFbn2zSQghygMJpEII4SIJpEII4SIJpEII4SIJpEII4SIJpEII4SIJpEII4SK3erNp3LhxWK1WTCYTI0aMICkpidDQUKeUI2fOnOHYsWO0atXqtvRp/Pjx5OXlYTAYeOedd25Lm0KI8sWtRqRxcXH89ttv+s//+c9/GDdunFOZ1atX8/LLL9/WfiUlJfH+++/f1jaFEOWHWwVSgKefflpPAwLw3nvvsWXLljvWn7feeotnnnnmjrUvhLjz3C6QFta0aVP69++vL6hcUF5eHq+++irBwcGEhITw2muvcfnyZQCWLl3KhAkTeOWVV/D39yc6OpqlS5cSExNDYGAgffv2dUqeN3nyZOrXr0+1atX485//XGxiPSHEvcmtA6mmaTRs2JAXXniBv/zlL0X2m81mPD09+fvf/87QoUOZPXs2Y8eOBSAlJYURI0bQuHFj1q1bR15eHgMGDOCTTz5h1apVrF27Vk9kN336dP73v//xzTffsG3bNpKTk4ttTwhxb3Krm00lGT58OO3bt2fGjBkYjUanfZ9++ik5OTns27ePhx56iD179uj7mjZtyuuvvw5Ar169WL58OS1btgTg0UcfZe/evYDjJteLL75Ibm4uSUlJ9O/fnz59+jBp0iQqV658m85SCFFeuXUgzV+4ymAwMHPmTNq3b8+gQYPQNA2ArKwsnnvuObZu3cqjjz7K77//XiTQ5ss/Jl/lypVJSUkhOzubgwcPsmjRIqdV8R9++GGysrLK6MyEEO7ErQNpQXXr1mXUqFEMHjyYsLAwAGbPns2ePXs4ePAgPj4+fP7553z77bc3Va/FYsHX15fhw4fzxBNPlEXXhRBuzq3nSAsbOHAg0dHRTtuys7PJy8vDarWyZs2am75JZDAY+NOf/sSYMWM4d+4cAAcOHODMmTO3rN9CCPd2VwVScNwYqlixIuB4VCo0NJQGDRpQp04dwsLCOHDgAJs2bbqpOidNmkTt2rWpWrUqmqbxwgsvsHXr1rLovhDCDbnVCvk+Pj7Mnj2bRx555LrlkpOTqVmzJuBIgrd7927CwsKoUKECSUlJVKxY8Q9l+zxy5AgAoaGhTttXrFjBoxOoKuAAACAASURBVI8+Sm5u7k3XKYRwf24XSJ966ikaNmzo9FD+nTR+/HgSExP57rvvJJAKcY9yq5tNMTExnD59mtTU1DvdFd3GjRvJysriwQcfvNNdEULcIW41IhVCiPLorrvZJIQQt5sEUiGEcJEEUiGEcJEEUiGEcJEEUiGEcJFbPf5UONVIvh07drBlyxbsdjstW7YkKiqqxMVJ3E1qaiqapjmtMpWTk8OmTZto27YtFoulzPvw/fffc/jwYQD69etHrVq1yrxNIdyJW41IC6caAZg5cyYtW7bkH//4B59//jlt2rRh27Ztd6iHt97mzZt5+umnKfiU2rlz5+jUqRMXLly4bf2wWq2MGjWK48eP37Y2hXAXbhVIwTnVyP79+3nppZeYNm0ahw4d4tdff+X333+ndevWd7iXt9bq1av55JNP7lj7zzzzTLl5k0yI8sjtAmlBY8eO5aGHHuLFF1/UtxV8Dz43N5dhw4YRHBxMrVq1GDJkCFarFYDnn3+eJUuWEBERwX333cf777/P2LFjCQkJISIigqVLl+r1PPvsswwZMoQGDRoQEhLC22+/jc1mA2DNmjXExMRQqVIloqOjWb58udNxixcvpmXLllStWpW33noLgKlTpzJy5Eincxk8eDBz584t9jyrVKnCtGnT2L59e5F9kk5FiHJAuRFvb2+1ZMkS/efGjRurTz75pMTyb775pgoPD1d79+5VS5cuVfXq1VNDhw5VSikVGhqqoqOj1f79+9WECRMUoN5880119uxZ9be//U35+/urrKwsveyf//xndf78ebVkyRIVHBysRo0apZRSau3ateqtt95S8+fPVz179lS+vr7q/Pnz+nFPP/20Sk5OVkuXLlW+vr5qyZIlas+ePcrT01OlpaUppZS6cOGCMpvNKjk5ucg5/Pjjj6pmzZpqw4YNKjw8XF25ckUlJycrQJ0+fVoppdTgwYPVl19+qSZPnqyqVKmihg8frpRSasqUKcpisah//vOfat++feqBBx5Q1apVU9u2bVM7d+5UtWvXVuPHj1dKKfXNN9+odu3aqQ0bNqjjx4+rrl27qmeffVbvR1ZWlgLUunXr/tgfnhB3MbcOpH5+fmrGjBkllq9QoYL63//+p//8ySefqGrVqimlHEFu4cKF+j5fX1/1888/K6WUSklJUYDav39/sWX/9a9/qdDQUKe2jh8/rn788UenYFP4uM6dO6uPPvpIKaVUmzZt1Oeff66UUuqLL75QMTExxZ5DfiBVSqnhw4erl19+uUggVUqp7OxstW3bNtWnTx/VrVs3pZQjkEZFRTmd/8MPP6z//Nprr+nBskGDBmrcuHHq559/Vj///LP63//+pwCVmpqqlJJAKsT1uNVd+8KqV69OSkpKsfsuX75Menq6vpweQIsWLUhJSdEv7wsqeJc/ICAAoMTVnOrUqaMvqbdw4UIGDRpEvXr1aNy4MRaLhUuXLhV7nI+PD9nZ2YDj7vf06dMZOHAgc+fOpU+fPsUeoxz/2AEwevRooqKiWLhwIeBIjyLpVIS489w6kDZr1ox58+YxdOjQIvsqVqxIxYoVSUhIoG3btgCcOnWK4OBgTCbXTnvTpk2EhIQAMGzYMIYOHaon0StpnrOw559/nr/97W+sXbuWDRs2MHv27BseY7FYmDFjhp4FQCkl6VSEKAfc+mbTBx98wKFDh+jXrx9HjhwhJSWFd999ly+++AKAnj17MmfOHFJTU8nNzWXatGl/OFDk5eWRk5PDsmXLmDNnDo8//ri+L3+UuWXLFi5cuFCqmzQ+Pj786U9/om/fvvTo0QN/f/9S9aNp06ZF7qBLOhUh7iy3DqShoaFs2rSJpKQk6tSpQ82aNdm9e7c+YouLi+PixYuEh4dTu3ZtsrKyGDVq1B9qq2/fvnh6etK9e3eqVKnCO++8AzieHHjnnXdo2rQpQ4cOpWvXrsyYMaNUdT7//PMcO3asxMv6kgwfPpw2bdoAkk5FiPLArdYjvV6qkWPHjlGhQoUieebtdjuJiYmYTCbCwsKKzBOWRp06dZg4cSJhYWF4eHhQt25dp/2nTp0iPT2d+vXrY7Va2bNnDy1btrxhvfv376djx46cPn0as9l8U306c+YM/v7+WCyW25JOJTs7Gy8vL9atW0f79u1vul4h7mZuN0c6a9Ys9u7dW+Tytnbt2sWWNxgMNGrU6Ja0XVI99913n/692WwuVRCNi4tj1qxZDBky5KaDKEBQUJD+vcFg4P7779d/Lhzob0bhAAqOV0QTExP/cJ1C3O3cKpDeqVQjrVu31u/k3yopKSmMHz/+hon8yoP9+/ezZcsWHn74YSpVqnSnuyNEueNWl/ZCCFEeufXNJiGEKA8kkAohhIskkAohhIskkAohhIskkAohhIvc6vGn4lKNJCQksHLlSmrVqkXHjh3/0EPoZe2nn37ioYceum6ZZcuWkZub6/Tqab5ff/2V1atXM2jQoLLqIgDjx48nLy8Pg8Ggv7klhLgxtxqRFk418sUXX3D//fczd+5cPvvsM33hZHAsUrxv374y68uBAweYOHHiDcudPn2aJ598kitXrly33IoVK1i8eHGJbU2ePPkP9fNmJSUl8f7779+WtoS4W7hVIAXnVCMffvghkydPZuPGjaxbt47PPvtMLzd8+PAyfXB/8eLFTqvol2Tq1KlcvnyZefPmlVlfbpW33nqLZ5555k53Qwi343aBtKDc3FynkZ6vry8AQ4YMITs7m2HDhtG5c2fOnTtHVlYWnTt3ZubMmYSHh+uLfhw/fpzHH3+cSpUq0bJlS1asWKHXl5WVxaBBg6hatSr169fno48+AmD37t1MnTqV3bt307lzZz799NNi+6eU4quvvqJv3758//33TvvsdjuDBw+mSpUqdOzYscgiI9OnT6devXqEh4czbdo0ffumTZt4+umn+eyzzwgKCtIvwePj42nTpg1+fn7Exsbq78yfPXuW/v37c99999GxY0d++uknbDYbo0aNon79+kRGRt620a4Qd607t6b0zSu8Qv7IkSOV2WxWH330kcrIyNC379q1S3l6eqrPPvtM/fzzzyo7O1ulp6crQLVp00YdPXpUJScnq+zsbNWkSRM1YcIEdfbsWTVz5kxVoUIFtWfPHqWUUgMGDFD9+/dXhw8fVhs2bFB16tRR//73v1VaWpoaOHCgioqKUj///LNKTEwstr/Lly9X4eHh6vTp08pkMqkTJ07o+8aMGaNatGihNm/erI4ePapatGihBgwYoJRSas2aNSooKEjNnz9fpaamqv79+6uwsDCllFJLlixRFotF9evXT507d06dO3dOJSYmqpCQEDV//nx17tw5NWzYMFW3bl2VmZmpxo4dq8LDw9XJkyfVokWL1NatW9W6desUoLZv3652796tZs+e7dRns9l86/7QhLgHuPWIdMyYMUybNo358+cTEhLCokWLAMeCzwaDgcjISGJiYvDw8NCPGTlyJLVr16ZGjRrMnj0bu91O8+bN2b9/PzVq1KBjx4588803HD9+nClTptC7d2+Sk5PJzc2lT58+TJkyBT8/P0JCQqhUqRIxMTE0aNCg2P5NmTKFPn36UK1aNTp06KAvr5eens7YsWP58ssveeCBB6hdu7bTikpxcXEMHz6cnj17Urly5WLfx584cSKBgYEEBgby6aef0rZtW/z9/UlISCA2Npbs7GxWrFhBUFAQJ06cIDExkccee4yoqCiCgoIwGAysWrWKyMhIevXqdSv/WIS457jVXfviPPfcczz77LO88847PP/886SkpODj41Ni+Tp16ujfHzhwgPPnz/PBBx84lalSpQq//vorJpOpyGV7WFhYqfp1/vx5FixYwPjx4wHHItOTJ09mxIgR7Nu3j6ysLCIiIoocp5Ri/fr1xa76n69q1apOC0EXdx4NGzbEYrHw4osv8ttvv9GzZ0969+7Nxx9/TFhYGN9++y0jRoxg2bJlfP3116U+LyFEUW4fSMGRi+j1119n3LhxHDp0iObNmwOOecjr8fPzo0aNGqxatarIvs2bN2O1Wpk1a1aRNU7zqeus9/L1119jtVqLLGm3efNmvL29Adi+fTvt2rUrci6enp7s2rWLbt26Xbf/Bc8jKiqKCRMmFLt/3LhxDBkyhCeffJIBAwYwb948nnnmGf70pz/x5ptv0q5dO06fPl1irichxPW59aX9hg0bSExM5Ny5c3zwwQdYLBZ9XdLatWuzc+fO6x7/yCOPsG/fPj01ic1m4+effwYc0wMNGzbkjTfe0JPlFUwMFxwcTGJiop5DviClFFOnTuXDDz/Uk9cppejcuTPfffcd4eHhhIaG8vHHH7Np0yaUUpw4cUI/vnv37vz3v/9l2bJl5OTkcPLkyeueR8+ePZkyZQrbtm0DIC0tjV27dgGOhZoPHTpEYGAg7dq148KFC1itVn766SfMZjOPPPIIqamp5OXlXbcNIcR13MH52ZtW+GZTnz59FKAAdd9996k5c+bo+2bNmqVMJpMC1Pvvv6/fbDpw4IBTnYsXL1YBAQEKUJUqVVJPPvmkvi8xMVE1btxYAcpkMqkOHTqoY8eOKaUc6Ylbt26tAGUwGFROTo5+3KpVqxSgjh8/7tRWft753NxctWHDBlWlShUFKF9fXxUZGanfbEpJSVFRUVH6ubVp08bpZlONGjWKfDbvvfeeXj40NFTFxcUppZSaO3eu8vX1VYCqWbOm2rhxozp06JCqWbOmApSXl5f66quv9HrkZpMQN8+t1iMtLtVIcnIyFy5cICIiosil6cmTJzl8+DDNmzenYsWKJdabnZ1NQkICTZs2LbJavd1uZ8+ePdSpU6fIosY2m42tW7fi4+NDZGTkTZ9PTk4Oe/fupXHjxvrlfuF2a9SoUeq3tdLS0jh+/DhNmzZ12p6VlUViYqKeLjq/7/v27SM0NNTpvFasWMGjjz5aYipqIURRbhdIn3rqKRo2bFgk1Yhw3fjx40lMTOS7776TQCrETXCrm013KtXIvWLjxo1kZWXx4IMP3umuCOFW3GpEKoQQ5ZFb37UXQojyQAKpEEK4SAKpEEK4SAKpEEK4SAKpEEK4yK0efyou1QjAjh072LJlC3a7nZYtWxIVFYXRaEQpxS+//EKLFi2oUKHCLe1LSkoKCxYswNPTk9atW9OwYcNbWv+tkpSURHJycpHtrVu3xtPTk6NHj5Kenk6TJk2uW8/GjRv1V2Q7duxIhw4dyqS/QrgjtwqkcXFx+gP5+WbOnEnfvn0JCwvDZDJx4MABNm3aROvWrTl79iydOnVi2bJldOvWjRUrVpCRkcGTTz7pUj+2bt1Kt27dqFWrFlWqVOHjjz/m119/BbjpNm5Vn0qyevVq5s6dq/+8f/9+AgIC2L17NwATJkxg165drFu3rlT1TZs2DZPJJIFUiILu4OupN63wu/YJCQnKZDKpadOm6dt+//13p2N27NihbDabUkqpHj16qNGjR7vcj759+6rnn39e/zk9PV3//mbbuFV9Ko309HRVpUoV9f333+vbzp8/r44cOVLqOqKjo9XYsWPLoHdCuC+3niMdO3YsDz30EC+++KK+LTQ0VP++W7duDB8+nNOnTzN9+nS2bt3KzJkz6dy5M6tXr2bq1KmMHDnSqc7Bgwc7jeCKY7Vai01xUlwbAJ9//jnNmzcnMDCQnj17cvDgweuWLynFiasmTZpE9erV6dOnDwDffvstffr00dOgLF26lOeee45nnnkGf39/OnbsqK8iJYQomVsH0j179tC5c+cS9w8aNIjVq1eTlZVFx44dCQsLo0OHDrzzzjs0adKEli1b8umnn3Lx4kXAsejH5MmTad269XXbfeGFF1iyZAn9+vUjJSVF315cGwAmk4lnnnmGSZMmceTIEV566aXrln/99de5cuUKmzZt4ptvvuHLL7/Ul/r7o65cucLEiROdMoRGR0cTGBioB/aUlBRWrlzJwIED2bNnD+Hh4XTt2pWzZ8+61LYQdzu3DqQnT56katWqJe5v27at/n1ISAj+/v7Url2bmJgYgoKCiIyMpHnz5vqIbPbs2bRr144aNWpct91u3bqxbt06rly5Qq1atXjvvfdQShXbBsBf/vIX/va3v9G0aVOefvpptm/fjt1uL7b89VKcuGLSpEmEhITwxBNP6Ntq167Nfffd51QuNDSUDh06UKtWLT7//HMsFgtLlixxqW0h7nZudbOpsOrVqzuNCP+Ifv36MX36dAYOHMjcuXP1y94bad26NfPmzWPp0qU88sgjdOjQocTFPkaOHMmUKVNo164dBoOB3NxcMjMz9SmBglxNcVKc/NFofs6o0jIajYSGhnL69Ok/3LYQ9wK3DqTNmjVj3rx5181vVFjh9CPPP/88f/vb31i7di0bNmxg9uzZN9WH7t27ExERwb59+/RAWrCNo0eP8sEHH7B3716aNGlCQkICc+bMKbFPfn5+N0xxcrMmTZpEgwYNSp26JF9GRgY7d+50moMWQhTl1pf2H3zwAYcOHaJfv34cOXKElJQU3n333RLnE4ODg9m9e7dT4PLx8eFPf/oTffv2pUePHvj7+7Nu3Tr69u3LhQsXiq3n119/ZfPm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PXuWJk2a6JePNyMjIwMfH58i2z09PQHIzMzUtzVo0IA33niD//u//+PHH3+86bbuRnPmzOGxxx7DYDDw0EMPsWjRIh566CHAcWnfrVs38vLyWLFiBQaDgVatWtGuXTs2btwI3NznXxpjxoyhe/fuTvPr9yoZkZYzx44dw263O92dHjhwIK+99hqvvfYaAQEB+nZN0+jduzfLli2jcuXK/OMf/wCgRo0anDhxosQ28kcUW7dupVOnTmzevPmG/erRowevvvoq33//PQsWLHAK9KVVvXr1Ykc9+X297777UErp71y/+OKL1KpVixEjRqBp2j09X5qWlsaKFSv0y/KHH36Y1atXc/nyZex2O7/++ivh4eHExsayatUq4uPj6dKlCw0bNmT37t1A6T7/0sj/M/L19eXjjz/mo48+4uDBg/f0n48E0nLm66+/pmXLljf17nFgYCA9e/bk8OHDALRs2ZIff/wRm81WbPmCwSouLo4TJ07ccD7yiSee4Pnnn6dVq1Z/+C/M/fffz44dOzh37pzT9pUrV9KhQ4ciwVnTND755BM2bNjA5cuX3XpRC1fNmjULm83GE088Qd26dXn55ZfJycnhxx9/5OjRo1y5coXw8HC6devG2rVrWb9+PV27diUiIoK0tDROnDhRqs8/MDAQwOnuel5eHufPn9cfkSrogQceoG/fvsybN++e/vORQFoObNu2jZUrVzJo0CDWrl3L+++/X2T/li1b9K/U1FTOnz/Pl19+SWZmJtu3b2fRokU0b94cgL/+9a9kZWXx17/+laSkJNLT0/nuu++Ii4sr0na1atUYMmQIkyZN4vTp02V6nrGxsTRq1Ig333yTpKQkLl68yFdffcUPP/zAq6++WuwxVatWLbbf9xKlFHPmzGHQoEEkJSXpX8899xyLFy/mwIED1KpVi0qVKhEQEECrVq1o0KABQUFBVKhQgdq1a7Nv375Sff5hYWFUq1aNCRMm6P8Q//Of/8Tb29vpWeCC3nzzzRvOyd/tJJCWAy+99BL/+c9/CAkJYenSpYSHhzvt79evH88++6z+tXnzZo4fP86UKVP0+crKlSvzxhtvAODv78/8+fPJyMigS5cuNGvWjCVLlvDwww8X237fvn2pX79+mQcsg8HA9OnT8ff3158qWLBgAdOnTy/yGE1BsbGxPPXUU2Xat/Js48aNHD16lJ49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6tSpo4KCAu3atUtNmzYt7cOCmyGkAGCJ90gBwBIhBQBLhBQALBFSALBESAHAEiEFAEvuc0UsgNuCpUZujOtI4Ta+/vpr7d27VwEBAXr00UdVv379EvdfvHixfHx81L59+zt0hK6HpUZuUinNzA/cUmPGjDEeHh6mWbNmJigoyHh5eZmZM2c6x/ft22cmT55c5D4xMTHm1VdfvdOH6lJYauTm8B4pXN4PP/yg8ePHa8OGDYqLi1N6erpefvllvfDCCzpy5Igkafny5frqq69K+Uhdz7Jly9S1a1fn5CJPPvmktm7des0JmZOTk7V792716dNH0pUZ8CMiIhQXF1fimDsgpHB5H3zwgbp166aIiAjntvHjxyskJERz5szRzp079emnn2rnzp2Kjo7WpEmTnPulpKQoMjJS9957rwYMGKCff/7ZOTZ16lTVqVNH9913nwYPHuycOervf/+73n77bcXExCgwMFDLly+/cyd7h7HUyM0hpHB5u3btKrbssqenp5o3b64DBw7I4XDo8ccfV40aNTRy5Eh17tzZuV9KSor++te/asOGDdq1a5fee+89SdKcOXM0f/58zZ49W1u3blVaWpqGDBkiSTp06JDGjx+vJk2aKCkpSe3atbtzJ3uHsdTIzSGkcHmnT5/WPffcU2z7Pffco+zsbAUEBMjhcMjf31+RkZGqW7euc5927dqpcePGatiwoXr37q2EhARJV57Rdu7cWXl5eUpJSdGgQYP02WefOZfaaNiwoV566SUFBQWpXLlyd+ZES8GtWGokKCioxDF3wOVPcHnVqlXT8ePHi20/cuSIatWqddOPU758eeXk5OjixYtKTk7W0qVL9e9//9s5HhUVpdzcXElS7dq17Q/cBfy3S43UrFnTuX+PHj1KHHMHPCOFy2vRooVzqeWrTp06pW+++UYdO3Z0bjM3eaWft7e3fH199cc//lFr164t8ic4OPiWHvvdjqVGbg4hhct77bXXtH37do0dO1bnzp3T3r171bNnT/3ud79Thw4dJEnBwcFKSkoq8mHS9Xh4eKhHjx56++23lZmZKUlKTEy85gcs7o6lRm5SaV9/BdwKW7duNaGhoUaS8fb2Nn379jVnz551jufm5ppHH33USDIeHh7m0qVLxa4jnTRpkomKijLGGHP+/Hnz1FNPGUlGkmnSpIlZtmyZMcaY2NhY079//zt7gqUsJyfHHD58uNj29PR0U1hYWGx7UlKSyc/Pv+ZjlTTmqvhmE9xKUlKSQkJC5OvrW2zs8uXLiouLU8WKFdWwYcOberzU1FRJUo0aNW7pccK9EFIAsMR7pABgiZACgCWuI4VL+vU66nB9rvwuIyGFS3Llf3RwP7y0BwBLhBQALBFSALDEe6QAnPLz85WRkaGQkBDntt27d2vDhg2qX7++2rZte8MP+lizCXAx8fHxWrly5XXHX3zxRfn4+Gj8+PHavXu3KlSooIYNG2r48OHy9va+qfsHBARox44dysjIcH53X5IOHjyoefPm6c0335Snp+s/JykoKFDPnj3Vpk0bvfTSS5KksWPH6v3339czzzyjXbt2KSAgQMuWLZOHx7VfzLJmE+CCVq5caaKiokxUVJRp1KiR8fT0dN6OiooyJ06cMD169DAPPPCAGT58uBk4cKAJCAgwrVq1Mnl5eTd1f2OMGTp0qOnatWuRn7169WojybmekStbs2aNcy6CKVOmOLd/99135siRI8YYY1JTU40ks3Hjxus+zm91zSZCCrcxf/584+/vX2TbggULjI+Pjzl48KBz2/bt20358uXNxx9/fMP7G2NMdna2CQgIMD4+PubkyZPO7e4U0mHDhpn58+eb0NBQ87e//e2a+xw7dqzEkCYlJRlJRSaL6datm/nTn/5U4pg74MMmuLV//OMf6t69u3MyYUl65JFH1K9fPy1duvSmHmPBggVq1qyZWrZsqc8+++x2HWqpmjp1qnr37i3p+tfozpkzRw6HQ82bN7/mOGs2AW4qJSVFoaGhxbbXqlVLhw8fvqnHmDVrlnr16qUnn3zSbUN6I8eOHdN7772niRMnXncOUdZsAtzU2bNn5efnV2x7hQoVlJ2dfcP779mzR1u2bFH37t3VvXt3xcXFKTEx8XYc6l1tyJAheuyxx0pcGuS3vGYTIYVbczgcOnbsWLHtP/30kxwOxw3vP3PmTHXq1EmBgYG6//77FRYWpjlz5tyGI717zZ8/X5s2bdKMGTNK3O+X6zJdtW/fPrVo0aLEMXdASOHWWrZsWWzd+cLCQi1dulRPPPFEife9dOmSPvvsMy1ZskRlypRRmTJl9OOPP2ru3Lm6fPny7Tzsu0ZWVpZeeeUV9erVSwcOHNCGDRu0adMmSazZ9EuEFG7t9ddf1+HDhzVixAhlZWUpJSVFMTExunDhgp5//vkS77tgwQJdvHhR58+fl7lyhYtOnDih9PR0rV+/3rnfd999pw0bNjj/uMsHKJIUGxur9PR0zZgxQ23atFGbNm2cH0qxZtMvlPJVA8Atc73Ll/bs2WNatGjhXK8pOjrapKam3vD+kZGRpl+/fsX2a926tYmJiXFe/vTrP/Pnz7+l53U3Y82mK/hmE34zUlJSFBgYqHvvvbe0DwVuhpACgCXeIwUAS4QUACwRUgCwREgBwBIhBQBLhBQALBFSALBESAHAEiEFAEuEFAAsEVIAsERIAcASIQUAS4QUACwRUgCwREgBwBIhBQBLhBQALBFSALBESAHAEiEFAEuEFAAsEVIAsERIAcASIQUAS4QUACwRUgCwREgBwBIhBQBLhBQALBFSALBESAHAEiEFAEuEFAAsEVIAsERIAcASIQUAS4QUACwRUgCwREgBwBIhBQBLhBQALBFSALBESAHAEiEFAEuEFAAsEVIAsERIAcASIQUAS4QUACwRUgCwREgBwNL/Bww6nVhM8tI9AAAAAElFTkSuQmCC", }, { - name: "Company-Invoice-2", - template_id: 3004, + name: "Mobile-Multi-Invoice", + template_id: 1003, ImageUri: - "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", }, // { // name: "Health-Log", diff --git a/src/templates.ts b/src/templates.ts index 6834610..5da6cfd 100644 --- a/src/templates.ts +++ b/src/templates.ts @@ -51,10 +51,10 @@ export interface TemplateData { index: number; isActive: boolean; }[]; - logoCell: string | { [footerIndex: number]: string }; - signatureCell: string | { [footerIndex: number]: string }; + logoCell: string | { [sheetName: string]: string }; + signatureCell: string | { [sheetName: string]: string }; cellMappings: { - [footerIndex: number]: CellMapping; + [sheetName: string]: CellMapping; }; } @@ -64,13 +64,13 @@ export let DATA: { [key: number]: TemplateData } = { templateId: 1001, footers: [{ name: "Invoice", index: 1, isActive: true }], logoCell: { - 1: "F5", + sheet1: "F5", }, signatureCell: { - 1: "D38", + sheet1: "D38", }, cellMappings: { - 1: { + sheet1: { Heading: "B2", Items: { Name: "Items", @@ -219,13 +219,13 @@ export let DATA: { [key: number]: TemplateData } = { templateId: 1002, footers: [{ name: "Invoice", index: 1, isActive: true }], logoCell: { - 1: "F7", + sheet1: "F7", }, signatureCell: { - 1: "E41", + sheet1: "E41", }, cellMappings: { - 1: { + sheet1: { Heading: "B2", Items: { Name: "Items", @@ -268,10 +268,10 @@ export let DATA: { [key: number]: TemplateData } = { msc: { numsheets: 1, - currentid: "sheet2", + currentid: "sheet1", currentname: "inv2", sheetArr: { - sheet2: { + sheet1: { sheetstr: { savestr: "version:1.5\ncell:B2:t:INVOICE:l:1:f:9:cf:1:colspan:6\ncell:C2:t::l:2:f:13\ncell:D2:t::l:2:f:13\ncell:E2:l:1:f:10:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:8:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:13\ncell:B3:f:6:cf:2:colspan:4\ncell:C3:t::l:2:f:13\ncell:D3:t::l:2:f:13\ncell:F3:l:1:f:7:cf:2\ncell:G3:l:1:f:14:cf:2:ntvf:3\ncell:B4:f:2:colspan:2\ncell:F4:t:DATE\\c:l:1:f:14:cf:2\ncell:G4:vtf:nd:45898:TODAY():l:1:f:14:cf:2:ntvf:3\ncell:B5:t:INVOICE #\\c:f:3:colspan:6\ncell:C5:cf:2:colspan:5\ncell:F5:l:1:f:7:cf:2:colspan:2\ncell:G5:l:1:f:14:cf:2\ncell:B6:f:2:colspan:2\ncell:F6:l:1:f:7:cf:2:colspan:2\ncell:G6:l:1:f:14:cf:2\ncell:B7:t:FROM\\c:f:12\ncell:F7:l:1:f:7:cf:2:tvf:4:colspan:2:rowspan:6\ncell:G7:l:1:f:14:cf:2\ncell:B8:t:[Company Name]:f:3:colspan:4\ncell:F8:l:1:f:7:cf:2:colspan:2\ncell:G8:l:1:f:14:cf:2\ncell:B9:t:[Street Address]:f:1:cf:2:colspan:4\ncell:F9:l:1:f:7\ncell:G9:l:1:f:14:cf:1\ncell:B10:t:[City, State, Zip]:f:1:cf:2:colspan:4\ncell:G10:l:1:f:13\ncell:B11:t:Phone\\c :f:1:cf:2:colspan:4\ncell:B12:t:Email\\c:f:1:cf:2:colspan:4\ncell:B13:colspan:2\ncell:B14:t:BILL TO\\c:f:11:cf:2\ncell:B15:t:[Name]:f:1:cf:2:colspan:6\ncell:B16:t:[Company Name]:f:1:cf:2:colspan:6\ncell:F16:t: \ncell:B17:t:[Street Address]:f:1:cf:2:colspan:6\ncell:B18:t:[City, State, Zip]:f:1:cf:2:colspan:6\ncell:B19:t:Phone\\c :f:1:cf:2:colspan:6\ncell:B20:t:Email\\c:f:1:cf:2:colspan:6\ncell:A22:b::1::\ncell:B22:t:DESCRIPTION:b:1:1:1:1:l:1:f:14:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C22:t::l:2:f:13\ncell:D22:t::l:2:f:13\ncell:E22:t::l:2:f:13\ncell:F22:t::l:2:f:13\ncell:G22:t:AMOUNT:b:1:1:1::l:1:f:14:c:1:bg:3:cf:1\ncell:A23:b::1::\ncell:B23:b:1:1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:4\ncell:D23:t::l:2:f:4\ncell:E23:t::l:2:f:4\ncell:F23:t::b::2:::l:1:f:4\ncell:G23:b::1::1:f:4:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:4\ncell:D24:t::l:2:f:4\ncell:E24:t::l:2:f:4\ncell:F24:t::b::2:::l:1:f:4\ncell:G24:b::1::1:f:4:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:4\ncell:D25:t::l:2:f:4\ncell:E25:t::l:2:f:4\ncell:F25:t::b::2:::l:1:f:4\ncell:G25:b::1::1:f:4:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:4\ncell:D26:t::l:2:f:4\ncell:E26:t::l:2:f:4\ncell:F26:t::b::2:::l:1:f:4\ncell:G26:b::1::1:f:4:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:4\ncell:D27:t::l:2:f:4\ncell:E27:t::l:2:f:4\ncell:F27:t::b::2:::l:1:f:4\ncell:G27:b::1::1:f:4:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:4\ncell:D28:t::l:2:f:4\ncell:E28:t::l:2:f:4\ncell:F28:t::b::2:::l:1:f:4\ncell:G28:b::1::1:f:4:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C29:t::l:2:f:4\ncell:D29:t::l:2:f:4\ncell:E29:t::l:2:f:4\ncell:F29:t::b::2:::l:1:f:4\ncell:G29:b::1::1:f:4:ntvf:1\ncell:A30:b::1::\ncell:B30:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C30:t::l:2:f:4\ncell:D30:t::l:2:f:4\ncell:E30:t::l:2:f:4\ncell:F30:t::b::2:::l:1:f:4\ncell:G30:b::1::1:f:4:ntvf:1\ncell:A31:b::1::\ncell:B31:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C31:t::l:2:f:4\ncell:D31:t::l:2:f:4\ncell:E31:t::l:2:f:4\ncell:F31:t::b::2:::l:1:f:4\ncell:G31:b::1::1:f:4:ntvf:1\ncell:A32:b::1::\ncell:B32:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C32:t::l:2:f:4\ncell:D32:t::l:2:f:4\ncell:E32:t::l:2:f:4\ncell:F32:t::b::2:::l:1:f:4\ncell:G32:b::1::1:f:4:ntvf:1\ncell:A33:b::1::\ncell:B33:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C33:t::l:2:f:4\ncell:D33:t::l:2:f:4\ncell:E33:t::l:2:f:4\ncell:F33:t::b::2:::l:1:f:4\ncell:G33:b::1::1:f:4:ntvf:1\ncell:A34:b::1::\ncell:B34:b::1::1:f:4:cf:2:colspan:5:rowspan:1\ncell:C34:l:2:f:4\ncell:D34:l:2:f:4\ncell:E34:l:2:f:4\ncell:F34:b::2:::l:1:f:4\ncell:G34:b::1::1:f:4:ntvf:1\ncell:A35:b::1::\ncell:B35:b::1:1:1:f:4:cf:2:colspan:5:rowspan:1\ncell:C35:t::b:::2::l:1:f:4\ncell:D35:t::b:::2::l:1:f:4\ncell:E35:t::b:::2::l:1:f:4\ncell:F35:t::b::2:2::l:1:f:4\ncell:G35:b::1:1:1:f:4:ntvf:1\ncell:B36:b:2::::l:1:f:13\ncell:C36:b:2::::l:1:f:13\ncell:D36:b:2::::l:1:f:13\ncell:E36:b:2::::l:1:f:14\ncell:F36:t:Subtotal:b:2::::l:1:f:11\ncell:G36:vtf:n:0:SUM(G23\\cG35):b:1::::f:11:ntvf:1\ncell:B37:t:NOTES:b:::1::l:1:f:14:cf:2:colspan:3:rowspan:1\ncell:C37:t::b:::2::l:1:f:13\ncell:D37:t::b:::2::l:1:f:13\ncell:F37:t:Tax Rate:l:1:f:11\ncell:G37:v:0:f:1:ntvf:2\ncell:B38:b:1::::f:1:cf:2:colspan:3\ncell:C38:t::b:2::::l:1:f:13\ncell:D38:t::b:2::::l:1:f:13\ncell:F38:t:Tax:l:1:f:11\ncell:G38:vtf:n:0:G37*G36:f:1:ntvf:1\ncell:B39:f:1:cf:2:colspan:3\ncell:C39:t::l:2:f:13\ncell:D39:t::l:2:f:13\ncell:F39:t:Other:b:::1::l:1:f:11\ncell:G39:v:0:b:::1::f:1:ntvf:1\ncell:B40:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C40:t::l:2:f:13\ncell:D40:t::l:2:f:13\ncell:F40:t:TOTAL:b:1::::l:1:f:14\ncell:G40:vtf:n:0:(G36+G38)+G39:b:1::::f:11:ntvf:1\ncell:E41:colspan:3:rowspan:3\ncell:E42:colspan:3:rowspan:2\ncell:B43:l:1:f:5:cf:1\ncell:C43:t::l:2:f:13\ncell:D43:t::l:2:f:13\ncell:E43:t::l:2:f:13\ncell:F43:t::l:2:f:13\ncell:G43:t::l:2:f:13\ncol:A:w:10\ncol:B:w:90\ncol:C:w:19\ncol:D:w:10\ncol:E:w:16\ncol:F:w:45\ncol:G:w:64\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:9:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:16:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nrow:36:h:14.25\nrow:37:h:14.25\nrow:38:h:14.25\nrow:39:h:14.25\nrow:40:h:14.25\nrow:43:h:15.75\nsheet:c:7:r:43:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 10pt Trebuchet MS\nfont:4:* 9pt Trebuchet MS\nfont:5:italic bold 10pt Trebuchet MS\nfont:6:italic normal * Trebuchet MS\nfont:7:normal bold 10pt Trebuchet MS\nfont:8:normal bold 14pt Trebuchet MS\nfont:9:normal bold 16pt Trebuchet MS\nfont:10:normal bold 28pt Trebuchet MS\nfont:11:normal normal * Trebuchet MS\nfont:12:normal normal 10pt *\nfont:13:normal normal 10pt Arial\nfont:14:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", @@ -400,22 +400,22 @@ export let DATA: { [key: number]: TemplateData } = { template: "Mobile-Multi-Invoice", templateId: 1003, footers: [ - { name: "Detail1", index: 1, isActive: false }, + { name: "Detail1", index: 1, isActive: true }, { name: "Detail2", index: 2, isActive: false }, - { name: "Invoice", index: 3, isActive: true }, + { name: "Invoice", index: 3, isActive: false }, ], logoCell: { - 1: "", - 2: "", - 3: "F8", + sheet1: "", + sheet2: "", + sheet3: "F8", }, signatureCell: { - 1: "", - 2: "", - 3: "F41", + sheet1: "", + sheet2: "", + sheet3: "F41", }, cellMappings: { - 1: { + sheet1: { Heading: "B2", Items: { Name: "Items", @@ -430,7 +430,7 @@ export let DATA: { [key: number]: TemplateData } = { }, }, }, - 2: { + sheet2: { Heading: "B2", Items: { Name: "Items", @@ -445,7 +445,7 @@ export let DATA: { [key: number]: TemplateData } = { }, }, }, - 3: { + sheet3: { Heading: "B2", Date: "G4", InvoiceNumber: "B5", @@ -730,13 +730,13 @@ export let DATA: { [key: number]: TemplateData } = { templateId: 3001, footers: [{ name: "Invoice", index: 1, isActive: true }], logoCell: { - 1: "F4", + sheet1: "F4", }, signatureCell: { - 1: "D31", + sheet1: "D31", }, cellMappings: { - 1: { + sheet1: { Heading: "B2", Date: "D6", InvoiceNumber: "D4", @@ -837,13 +837,13 @@ export let DATA: { [key: number]: TemplateData } = { templateId: 3002, footers: [{ name: "Invoice", index: 1, isActive: true }], logoCell: { - 1: "F4", + sheet1: "F4", }, signatureCell: { - 1: "D31", + sheet1: "D31", }, cellMappings: { - 1: { + sheet1: { Heading: "B2", Date: "D6", InvoiceNumber: "D4", @@ -960,13 +960,13 @@ export let DATA: { [key: number]: TemplateData } = { templateId: 3003, footers: [{ name: "Invoice", index: 1, isActive: true }], logoCell: { - 1: "F4", + sheet1: "F4", }, signatureCell: { - 1: "C36", + sheet1: "C36", }, cellMappings: { - 1: { + sheet1: { Heading: "F2", CompanyName: "B2", CompanySlogan: "B3", @@ -1007,10 +1007,10 @@ export let DATA: { [key: number]: TemplateData } = { }, msc: { numsheets: 1, - currentid: "sheet3", + currentid: "sheet1", currentname: "typeiii", sheetArr: { - sheet3: { + sheet1: { sheetstr: { savestr: "version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:3:cf:2:colspan:3\ncell:C2:t::l:2:f:9\ncell:D2:t::l:2:f:9\ncell:E2:l:1:f:7:c:2:cf:3\ncell:F2:t:INVOICE:l:1:f:7:c:2:cf:2:colspan:2\ncell:G2:t::l:2:f:9\ncell:B3:t:[Company Slogan]:f:4:cf:2:colspan:3\ncell:C3:t::l:2:f:9\ncell:D3:t::l:2:f:9\ncell:B4:f:2:colspan:2\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:2\ncell:F5:l:1:f:6\ncell:G5:l:1:f:10:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:G6:l:1:f:9\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:2\ncell:B8:t:Email\\c:f:1:cf:2:colspan:2\ncell:B9:colspan:2\ncell:F9:t:DATE \\c:l:1:f:6:cf:2\ncell:G9:l:1:f:10:cf:2:ntvf:3\ncell:B10:t:BILL TO\\c:f:5:c:1:bg:3:cf:2:colspan:2\ncell:F10:t:INVOICE # \\c:l:1:f:6:cf:2\ncell:G10:v:1:l:1:f:10:cf:2\ncell:B11:t:[Name]:f:1:cf:2:colspan:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:2\ncell:F12:t: \ncell:B13:t:[Street Address]:f:1:cf:2:colspan:2\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:2\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:2\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:6:c:1:bg:3:cf:1:colspan:5:rowspan:1\ncell:C17:t::l:2:f:9\ncell:D17:t::l:2:f:9\ncell:E17:t::l:2:f:9\ncell:F17:t::l:2:f:9\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:6:c:1:bg:3:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C18:t::l:2:f:9\ncell:D18:t::l:2:f:9\ncell:E18:t::l:2:f:9\ncell:F18:t::b::2:::l:1:f:9\ncell:G18:b::1::1:f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C19:t::l:2:f:9\ncell:D19:t::l:2:f:9\ncell:E19:t::l:2:f:9\ncell:F19:t::b::2:::l:1:f:9\ncell:G19:b::1::1:f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C20:t::l:2:f:9\ncell:D20:t::l:2:f:9\ncell:E20:t::l:2:f:9\ncell:F20:t::b::2:::l:1:f:9\ncell:G20:b::1::1:f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C21:t::l:2:f:9\ncell:D21:t::l:2:f:9\ncell:E21:t::l:2:f:9\ncell:F21:t::b::2:::l:1:f:9\ncell:G21:b::1::1:f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C22:t::l:2:f:9\ncell:D22:t::l:2:f:9\ncell:E22:t::l:2:f:9\ncell:F22:t::b::2:::l:1:f:9\ncell:G22:b::1::1:f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C23:t::l:2:f:9\ncell:D23:t::l:2:f:9\ncell:E23:t::l:2:f:9\ncell:F23:t::b::2:::l:1:f:9\ncell:G23:b::1::1:f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C24:t::l:2:f:9\ncell:D24:t::l:2:f:9\ncell:E24:t::l:2:f:9\ncell:F24:t::b::2:::l:1:f:9\ncell:G24:b::1::1:f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C25:t::l:2:f:9\ncell:D25:t::l:2:f:9\ncell:E25:t::l:2:f:9\ncell:F25:t::b::2:::l:1:f:9\ncell:G25:b::1::1:f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C26:t::l:2:f:9\ncell:D26:t::l:2:f:9\ncell:E26:t::l:2:f:9\ncell:F26:t::b::2:::l:1:f:9\ncell:G26:b::1::1:f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C27:t::l:2:f:9\ncell:D27:t::l:2:f:9\ncell:E27:t::l:2:f:9\ncell:F27:t::b::2:::l:1:f:9\ncell:G27:b::1::1:f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:5:rowspan:1\ncell:C28:t::l:2:f:9\ncell:D28:t::l:2:f:9\ncell:E28:t::l:2:f:9\ncell:F28:t::b::2:::l:1:f:9\ncell:G28:b::1::1:f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:5:rowspan:1\ncell:C29:t::b:::2::l:1:f:9\ncell:D29:t::b:::2::l:1:f:9\ncell:E29:t::b:::2::l:1:f:9\ncell:F29:t::b::2:2::l:1:f:9\ncell:G29:b::1:1:1:f:1:ntvf:1\ncell:B30:b:2::::l:1:f:9\ncell:C30:b:2::::l:1:f:9\ncell:D30:b:2::::l:1:f:9\ncell:E30:b:2::::l:1:f:10\ncell:F30:t:Subtotal:b:2::::l:1:f:10\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:8:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:6:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:9\ncell:D31:t::b:::2::l:1:f:9\ncell:F31:t:Tax Rate:l:1:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3\ncell:C32:t::b:2::::l:1:f:9\ncell:D32:t::b:2::::l:1:f:9\ncell:F32:t:Tax:l:1:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3\ncell:C33:t::l:2:f:9\ncell:D33:t::l:2:f:9\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:b:::1::f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:9\ncell:D34:t::l:2:f:9\ncell:F34:t:TOTAL:b:2::::l:1:f:6\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:5:ntvf:1\ncell:C36:tvf:4:colspan:5:rowspan:7\ncol:A:w:40\ncol:B:w:232\ncol:C:w:53\ncol:D:w:90\ncol:E:w:54\ncol:F:w:91\ncol:G:w:99\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nsheet:c:7:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncellformat:3:right\ncolor:1:rgb(0, 0, 0)\ncolor:2:rgb(0,0,0)\ncolor:3:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 10pt *\nfont:3:* 16pt Trebuchet MS\nfont:4:italic normal * Trebuchet MS\nfont:5:normal bold * Trebuchet MS\nfont:6:normal bold 10pt Trebuchet MS\nfont:7:normal bold 28pt Trebuchet MS\nfont:8:normal normal * Trebuchet MS\nfont:9:normal normal 10pt Arial\nfont:10:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n", @@ -1084,13 +1084,13 @@ export let DATA: { [key: number]: TemplateData } = { templateId: 3004, footers: [{ name: "Invoice", index: 1, isActive: true }], logoCell: { - 1: "F4", + sheet1: "F4", }, signatureCell: { - 1: "E36", + sheet1: "E36", }, cellMappings: { - 1: { + sheet1: { Heading: "F2", CompanyName: "B2", CompanySlogan: "B3", @@ -1132,10 +1132,10 @@ export let DATA: { [key: number]: TemplateData } = { }, msc: { numsheets: 1, - currentid: "sheet4", + currentid: "sheet1", currentname: "typeiv", sheetArr: { - sheet4: { + sheet1: { sheetstr: { savestr: 'version:1.5\ncell:A1:l:3\ncell:B2:t:[Company Name]:l:1:f:2:cf:2:colspan:3:rowspan:1\ncell:C2:t::l:2:f:7\ncell:D2:t::l:2:f:7\ncell:F2:t:INVOICE:l:1:f:6:c:1:cf:2:colspan:2\ncell:G2:t::l:2:f:7\ncell:B3:t:[Company slogan]:f:3:cf:2:colspan:3:rowspan:1\ncell:C3:t::l:2:f:7\ncell:D3:t::l:2:f:7\ncell:B4:cf:2:colspan:3:rowspan:1\ncell:F4:tvf:4:rowspan:4\ncell:B5:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:F5:l:1:f:5\ncell:G5:l:1:f:8:cf:1\ncell:B6:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:G6:l:1:f:7\ncell:B7:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B8:t:Email\\c:f:1:cf:2:colspan:3:rowspan:1\ncell:B9:cf:2:colspan:3:rowspan:1\ncell:B10:t:BILL TO\\c:l:1:f:5:bg:2:cf:2:colspan:2\ncell:F10:t:DATE\\c:l:1:f:5:cf:2\ncell:G10:l:1:f:8:cf:2:ntvf:3\ncell:B11:t:[Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:F11:t:INVOICE # \\c:l:1:f:5:cf:2\ncell:G11:v:1:l:1:f:8:cf:2\ncell:B12:t:[Company Name]:f:1:cf:2:colspan:3:rowspan:1\ncell:J12:tvf:4\ncell:B13:t:[Street Address]:f:1:cf:2:colspan:3:rowspan:1\ncell:B14:t:[City, State, Zip]:f:1:cf:2:colspan:3:rowspan:1\ncell:B15:t:Phone\\c :f:1:cf:2:colspan:3:rowspan:1\ncell:B16:cf:2:colspan:3:rowspan:1\ncell:A17:b::1::\ncell:B17:t:DESCRIPTION:b:1:1:1:1:l:1:f:5:bg:2:cf:1:colspan:3:rowspan:1\ncell:C17:t::b:1::1::l:1:f:7:bg:2\ncell:D17:t::b:1::1::l:1:f:7:bg:2\ncell:E17:t:HOURS:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:F17:t:RATE:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:G17:t:AMOUNT:b:1:1:1::l:1:f:5:bg:2:cf:1\ncell:A18:b::1::\ncell:B18:b:1:1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C18:t::b:2::::l:1:f:7\ncell:D18:t::b:2:2:::l:1:f:7\ncell:E18:b:1:1::1:f:1:ntvf:1\ncell:F18:b:1:1::1:f:1:ntvf:1\ncell:G18:vtf:t::IF(E18*F18>0,E18*F18,""):b:1:1:::f:1:ntvf:1\ncell:A19:b::1::\ncell:B19:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C19:t::l:2:f:7\ncell:D19:t::b::2:::l:1:f:7\ncell:E19:b::1::1:f:1:ntvf:1\ncell:F19:b::1::1:f:1:ntvf:1\ncell:G19:vtf:t::IF(E19*F19>0,E19*F19,""):b::1:::f:1:ntvf:1\ncell:A20:b::1::\ncell:B20:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C20:t::l:2:f:7\ncell:D20:t::b::2:::l:1:f:7\ncell:E20:b::1::1:f:1:ntvf:1\ncell:F20:b::1::1:f:1:ntvf:1\ncell:G20:vtf:t::IF(E20*F20>0,E20*F20,""):b::1:::f:1:ntvf:1\ncell:A21:b::1::\ncell:B21:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C21:t::l:2:f:7\ncell:D21:t::b::2:::l:1:f:7\ncell:E21:b::1::1:f:1:ntvf:1\ncell:F21:b::1::1:f:1:ntvf:1\ncell:G21:vtf:t::IF(E21*F21>0,E21*F21,""):b::1:::f:1:ntvf:1\ncell:A22:b::1::\ncell:B22:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C22:t::l:2:f:7\ncell:D22:t::b::2:::l:1:f:7\ncell:E22:b::1::1:f:1:ntvf:1\ncell:F22:b::1::1:f:1:ntvf:1\ncell:G22:vtf:t::IF(E22*F22>0,E22*F22,""):b::1:::f:1:ntvf:1\ncell:A23:b::1::\ncell:B23:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C23:t::l:2:f:7\ncell:D23:t::b::2:::l:1:f:7\ncell:E23:b::1::1:f:1:ntvf:1\ncell:F23:b::1::1:f:1:ntvf:1\ncell:G23:vtf:t::IF(E23*F23>0,E23*F23,""):b::1:::f:1:ntvf:1\ncell:A24:b::1::\ncell:B24:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C24:t::l:2:f:7\ncell:D24:t::b::2:::l:1:f:7\ncell:E24:b::1::1:f:1:ntvf:1\ncell:F24:b::1::1:f:1:ntvf:1\ncell:G24:vtf:t::IF(E24*F24>0,E24*F24,""):b::1:::f:1:ntvf:1\ncell:A25:b::1::\ncell:B25:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C25:t::l:2:f:7\ncell:D25:t::b::2:::l:1:f:7\ncell:E25:b::1::1:f:1:ntvf:1\ncell:F25:b::1::1:f:1:ntvf:1\ncell:G25:vtf:t::IF(E25*F25>0,E25*F25,""):b::1:::f:1:ntvf:1\ncell:A26:b::1::\ncell:B26:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C26:t::l:2:f:7\ncell:D26:t::b::2:::l:1:f:7\ncell:E26:b::1::1:f:1:ntvf:1\ncell:F26:b::1::1:f:1:ntvf:1\ncell:G26:vtf:t::IF(E26*F26>0,E26*F26,""):b::1:::f:1:ntvf:1\ncell:A27:b::1::\ncell:B27:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C27:t::l:2:f:7\ncell:D27:t::b::2:::l:1:f:7\ncell:E27:b::1::1:f:1:ntvf:1\ncell:F27:b::1::1:f:1:ntvf:1\ncell:G27:vtf:t::IF(E27*F27>0,E27*F27,""):b::1:::f:1:ntvf:1\ncell:A28:b::1::\ncell:B28:b::1::1:f:1:cf:2:colspan:3:rowspan:1\ncell:C28:t::l:2:f:7\ncell:D28:t::b::2:::l:1:f:7\ncell:E28:b::1::1:f:1:ntvf:1\ncell:F28:b::1::1:f:1:ntvf:1\ncell:G28:vtf:t::IF(E28*F28>0,E28*F28,""):b::1:::f:1:ntvf:1\ncell:A29:b::1::\ncell:B29:b::1:1:1:f:1:cf:2:colspan:3:rowspan:1\ncell:C29:t::b:::2::l:1:f:7\ncell:D29:t::b::2:2::l:1:f:7\ncell:E29:b::1:1:1:f:1:ntvf:1\ncell:F29:b::1:1:1:f:1:ntvf:1\ncell:G29:vtf:t::IF(E29*F29>0,E29*F29,""):b::1:::f:1:ntvf:1\ncell:B30:b:2::::l:1:f:8:cf:1:colspan:3:rowspan:1\ncell:C30:t::b:2::::l:1:f:7\ncell:D30:t::b:2::::l:1:f:7\ncell:E30:b:2::::l:1:f:8\ncell:F30:t:Subtotal:b:1::::f:1\ncell:G30:vtf:n:0:SUM(G18\\cG29):b:1::::f:1:ntvf:1\ncell:B31:t:NOTES:b:::2::l:1:f:5:cf:2:colspan:3:rowspan:1\ncell:C31:t::b:::2::l:1:f:7\ncell:D31:t::b:::2::l:1:f:7\ncell:F31:t:Tax Rate:f:1\ncell:G31:v:0:f:1:ntvf:2\ncell:B32:b:1::::f:1:cf:2:colspan:3:rowspan:1\ncell:C32:t::b:2::::l:1:f:7\ncell:D32:t::b:2::::l:1:f:7\ncell:F32:t:Tax:f:1\ncell:G32:vtf:n:0:G31*G30:f:1:ntvf:1\ncell:B33:f:1:cf:2:colspan:3:rowspan:1\ncell:C33:t::l:2:f:7\ncell:D33:t::l:2:f:7\ncell:F33:t:Other:b:::1::l:1:f:1\ncell:G33:v:0:b:::1::f:1:ntvf:1\ncell:B34:f:1:cf:2:colspan:3:rowspan:1\ncell:C34:t::l:2:f:7\ncell:D34:t::l:2:f:7\ncell:F34:t:TOTAL:b:1::::l:1:f:4\ncell:G34:vtf:n:0:(G30+G32)+G33:b:1::::f:4:ntvf:1\ncell:B35:b:2::::l:1:f:7\ncell:C35:b:2::::l:1:f:7\ncell:D35:b:2::::l:1:f:7\ncell:C36:tvf:4\ncell:E36:colspan:3:rowspan:4\ncol:A:w:40\ncol:B:w:194\ncol:C:w:128\ncol:D:w:60\ncol:E:w:65\ncol:F:w:95\ncol:G:w:90\nrow:1:h:34.5\nrow:2:h:34.5\nrow:3:h:14.25\nrow:4:h:14.25\nrow:5:h:14.25\nrow:6:h:14.25\nrow:7:h:14.25\nrow:8:h:14.25\nrow:10:h:14.25\nrow:11:h:14.25\nrow:12:h:14.25\nrow:13:h:14.25\nrow:14:h:14.25\nrow:15:h:14.25\nrow:17:h:14.25\nrow:18:h:14.25\nrow:19:h:14.25\nrow:20:h:14.25\nrow:21:h:14.25\nrow:22:h:14.25\nrow:23:h:14.25\nrow:24:h:14.25\nrow:25:h:14.25\nrow:26:h:14.25\nrow:27:h:14.25\nrow:28:h:14.25\nrow:29:h:14.25\nrow:30:h:14.25\nrow:31:h:14.25\nrow:32:h:14.25\nrow:33:h:14.25\nrow:34:h:14.25\nrow:35:h:14.25\nsheet:c:10:r:36:h:12.75\nborder:1:1px solid rgb(0,0,0)\nborder:2:thin solid rgb(0,0,0)\ncellformat:1:center\ncellformat:2:left\ncolor:1:rgb(0,0,0)\ncolor:2:rgb(221, 221, 221)\nfont:1:* * Trebuchet MS\nfont:2:* 16pt Trebuchet MS\nfont:3:italic normal * Trebuchet MS\nfont:4:normal bold * Trebuchet MS\nfont:5:normal bold 10pt Trebuchet MS\nfont:6:normal bold 28pt Trebuchet MS\nfont:7:normal normal 10pt Arial\nfont:8:normal normal 10pt Trebuchet MS\nlayout:1:padding:* * * *;vertical-align:bottom;\nlayout:2:padding:* * * *;vertical-align:middle;\nlayout:3:padding:36px * 28px *;vertical-align:*;\nvalueformat:1:#,##0.00\nvalueformat:2:#,##0.00%\nvalueformat:3:m/d/yy\nvalueformat:4:text-html\n', diff --git a/src/utils/settings.ts b/src/utils/settings.ts new file mode 100644 index 0000000..5cad052 --- /dev/null +++ b/src/utils/settings.ts @@ -0,0 +1,42 @@ +// Settings utility functions for managing app preferences + +interface AppSettings { + autoSaveEnabled: boolean; +} + +const SETTINGS_KEY = "app_settings"; + +const defaultSettings: AppSettings = { + autoSaveEnabled: true, // Default to enabled +}; + +export const getSettings = (): AppSettings => { + try { + const stored = localStorage.getItem(SETTINGS_KEY); + if (stored) { + const parsed = JSON.parse(stored); + return { ...defaultSettings, ...parsed }; + } + } catch (error) { + console.warn("Failed to load settings from localStorage:", error); + } + return defaultSettings; +}; + +export const saveSettings = (settings: Partial): void => { + try { + const currentSettings = getSettings(); + const newSettings = { ...currentSettings, ...settings }; + localStorage.setItem(SETTINGS_KEY, JSON.stringify(newSettings)); + } catch (error) { + console.warn("Failed to save settings to localStorage:", error); + } +}; + +export const getAutoSaveEnabled = (): boolean => { + return getSettings().autoSaveEnabled; +}; + +export const setAutoSaveEnabled = (enabled: boolean): void => { + saveSettings({ autoSaveEnabled: enabled }); +}; From 07b5a86c59ca904b7b39e024c5368650557dd279 Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Sun, 31 Aug 2025 17:33:26 +0530 Subject: [PATCH 8/9] dynamic form input --- DYNAMIC_FORM_UPDATE_SUMMARY.md | 142 ++++ SHEET_FORM_EXAMPLE.md | 107 +++ src/components/DynamicInvoiceForm.tsx | 213 +++-- src/components/FileMenu/FileOptions.tsx | 184 ++--- src/components/Files/Files.tsx | 7 +- .../TemplateModal/TemplateModal.tsx | 769 ++++++++++++++++++ src/components/socialcalc/modules/invoice.js | 183 +++++ src/contexts/InvoiceContext.tsx | 49 +- src/pages/FilesPage.tsx | 113 +-- src/pages/Home.tsx | 18 + src/utils/dynamicFormManager.ts | 20 +- src/utils/sheetChangeMonitor.ts | 118 +++ 12 files changed, 1624 insertions(+), 299 deletions(-) create mode 100644 DYNAMIC_FORM_UPDATE_SUMMARY.md create mode 100644 SHEET_FORM_EXAMPLE.md create mode 100644 src/components/TemplateModal/TemplateModal.tsx create mode 100644 src/utils/sheetChangeMonitor.ts diff --git a/DYNAMIC_FORM_UPDATE_SUMMARY.md b/DYNAMIC_FORM_UPDATE_SUMMARY.md new file mode 100644 index 0000000..7c0c49a --- /dev/null +++ b/DYNAMIC_FORM_UPDATE_SUMMARY.md @@ -0,0 +1,142 @@ +# Dynamic Form Component Update Summary + +## Overview + +Successfully updated the dynamic form component to support automatic form type changes based on the current sheet ID, eliminating the need for manual footer selection. + +## Key Changes Made + +### 1. **InvoiceContext.tsx** - Enhanced Context with Sheet Tracking + +```typescript +// Added new state and functionality +const [currentSheetId, setCurrentSheetId] = useState(null); + +// Auto-update sheet ID when template changes +const updateActiveTemplateData = (templateData: TemplateData | null) => { + setActiveTemplateData(templateData); + if (templateData && templateData.msc.currentid) { + setCurrentSheetId(templateData.msc.currentid); + } +}; +``` + +### 2. **DynamicFormManager.ts** - Sheet-Based Form Generation + +```typescript +// New method for sheet-based form sections +static getFormSectionsForSheet( + template: TemplateData, + sheetId: string +): DynamicFormSection[] { + const cellMappings = template.cellMappings[sheetId]; + if (!cellMappings) return []; + return this.generateFormSections(cellMappings); +} +``` + +### 3. **DynamicInvoiceForm.tsx** - Automatic Form Switching + +```typescript +// Removed footer selection, now uses current sheet automatically +const { activeTemplateData, currentSheetId } = useInvoice(); + +const effectiveSheetId = useMemo(() => { + return currentSheetId || currentTemplate?.msc?.currentid || "sheet1"; +}, [currentSheetId, currentTemplate]); + +const formSections = useMemo(() => { + if (!currentTemplate) return []; + return DynamicFormManager.getFormSectionsForSheet(currentTemplate, effectiveSheetId); +}, [currentTemplate, effectiveSheetId]); +``` + +### 4. **SheetChangeMonitor.ts** - New Utility for Real-time Sheet Detection + +```typescript +export class SheetChangeMonitor { + static initialize(updateSheetId: (sheetId: string) => void) { + // Polls SocialCalc every 500ms to detect sheet changes + this.intervalId = setInterval(() => { + this.checkCurrentSheet(); + }, 500); + } + + private static checkCurrentSheet() { + const control = SocialCalc.GetCurrentWorkBookControl(); + const currentSheetId = control.currentSheetButton.id; + + if (currentSheetId !== this.lastKnownSheetId) { + this.updateSheetIdCallback(currentSheetId); + } + } +} +``` + +### 5. **Home.tsx** - Integration with Sheet Monitor + +```typescript +// Initialize sheet change monitor after app loads +useEffect(() => { + if (fileName && activeTemplateData) { + const timer = setTimeout(() => { + SheetChangeMonitor.initialize(updateCurrentSheetId); + }, 1000); + + return () => { + clearTimeout(timer); + SheetChangeMonitor.cleanup(); + }; + } +}, [fileName, activeTemplateData, updateCurrentSheetId]); +``` + +## How It Works + +1. **Sheet Detection**: The `SheetChangeMonitor` continuously monitors SocialCalc for sheet changes +2. **Context Update**: When a sheet change is detected, the `currentSheetId` in the React context is updated +3. **Form Re-generation**: The `DynamicInvoiceForm` automatically re-renders with the appropriate form fields for the new sheet +4. **Persistence**: The current sheet ID is saved to localStorage for session persistence + +## Form Structure Examples + +### Sheet 1 (Service Invoice) + +- **Heading**: General heading field +- **Items**: Description, Hours, Rate columns + +### Sheet 2 (Product Invoice) + +- **Heading**: General heading field +- **Items**: Description, Qty, Price columns (different from Sheet 1) + +### Sheet 3 (Detailed Invoice) + +- **Heading**: General heading field +- **Date**: Invoice date +- **Invoice Number**: Invoice identifier +- **From**: Company details (Name, Address, Phone, Email) +- **Bill To**: Customer details (Name, Address, Phone, Email) +- **Tax Percentage**: Tax rate +- **Other Charges**: Additional charges +- **Notes**: Multiple note fields + +## Benefits + +✅ **Automatic Form Switching**: No manual footer selection required +✅ **Real-time Updates**: Form changes immediately when sheets are switched +✅ **Better UX**: Seamless integration with spreadsheet navigation +✅ **Type Safety**: Full TypeScript support with proper interfaces +✅ **Persistence**: Sheet state is maintained across sessions +✅ **Error Handling**: Graceful fallbacks when sheet data is unavailable + +## Testing + +The implementation includes: + +- Proper error handling for missing sheet data +- Fallback to default sheet ("sheet1") when current sheet is unavailable +- Cleanup functions to prevent memory leaks +- Console logging for debugging during development + +This update provides a much more intuitive user experience where the form automatically adapts to the current spreadsheet context without requiring manual intervention. diff --git a/SHEET_FORM_EXAMPLE.md b/SHEET_FORM_EXAMPLE.md new file mode 100644 index 0000000..80a2f37 --- /dev/null +++ b/SHEET_FORM_EXAMPLE.md @@ -0,0 +1,107 @@ +// Example showing how the form structure changes based on sheet ID + +// Sheet 1 Form Structure: +const sheet1Form = { +"Heading": "B2", +"Items": { +"Name": "Items", +"Rows": { "start": 6, "end": 18 }, +"Columns": { +"Description": "B", +"Hours": "E", +"Rate": "F" +} +} +}; + +// Sheet 2 Form Structure: +const sheet2Form = { +"Heading": "B2", +"Items": { +"Name": "Items", +"Rows": { "start": 6, "end": 18 }, +"Columns": { +"Description": "B", +"Qty": "E", // Different field: Qty instead of Hours +"Price": "F" // Different field: Price instead of Rate + } +} +}; + +// Sheet 3 Form Structure: +const sheet3Form = { +"Heading": "B2", +"Date": "G4", +"InvoiceNumber": "B5", +"From": { +"CompanyName": "B8", +"StreetAddress": "B9", +"CityStateZip": "B10", +"Phone": "B11", +"Email": "B12" +}, +"BillTo": { +"Name": "B15", +"CompanyName": "B16", +"StreetAddress": "B17", +"CityStateZip": "B18", +"Phone": "B19", +"Email": "B20" +}, +"TaxPercentage": "G37", +"OtherCharges": "G39", +"Notes": { +"1": "B38", +"2": "B39", +"3": "B40" +} +}; + +/\* +Key Changes Made: + +1. InvoiceContext.tsx: + + - Added currentSheetId state to track the active sheet + - Added updateCurrentSheetId function to update sheet ID + - Automatically sets sheet ID when template data changes + - Persists sheet ID to localStorage + +2. DynamicFormManager.ts: + + - Added getFormSectionsForSheet() method to get forms by sheet ID + - Modified convertToSpreadsheetFormat() to accept sheet ID parameter + +3. DynamicInvoiceForm.tsx: + + - Removed footer selection dropdown (no longer needed) + - Uses currentSheetId from context instead of activeFooterIndex + - Automatically generates form based on current sheet + - Shows current sheet ID in the form header + +4. SheetChangeMonitor.ts (New): + + - Monitors SocialCalc for sheet changes + - Automatically updates React context when user switches sheets + - Polls every 500ms to detect sheet changes + +5. Home.tsx: + - Initializes the sheet change monitor + - Connects sheet changes to the React context + +How it works: + +- When user switches sheets in SocialCalc, the monitor detects the change +- The currentSheetId in context gets updated automatically +- The DynamicInvoiceForm re-renders with the new sheet's form fields +- No manual footer selection needed - it's all automatic based on sheet + +Benefits: + +- Seamless integration with sheet switching +- No need for manual footer selection +- Form automatically adapts to current sheet structure +- Better user experience with real-time sheet-form synchronization + \*/ + +export { sheet1Form, sheet2Form, sheet3Form }; diff --git a/src/components/DynamicInvoiceForm.tsx b/src/components/DynamicInvoiceForm.tsx index 9b51db2..fd2b8f5 100644 --- a/src/components/DynamicInvoiceForm.tsx +++ b/src/components/DynamicInvoiceForm.tsx @@ -12,8 +12,6 @@ import { IonButtons, IonIcon, IonGrid, - IonRow, - IonCol, IonCard, IonCardHeader, IonCardTitle, @@ -22,22 +20,21 @@ import { IonToast, IonItemDivider, IonTextarea, - IonSelect, - IonSelectOption, IonChip, } from "@ionic/react"; import { close, save, trash, layers } from "ionicons/icons"; -import { TemplateData } from "../templates"; import { useInvoice } from "../contexts/InvoiceContext"; import { addInvoiceData, + addDynamicInvoiceData, clearInvoiceData, + clearDynamicInvoiceData, } from "./socialcalc/modules/invoice.js"; -import { - DynamicFormManager, - DynamicFormSection, +import { + DynamicFormManager, + DynamicFormSection, DynamicFormField, - ProcessedFormData + ProcessedFormData, } from "../utils/dynamicFormManager"; import "./InvoiceForm.css"; @@ -46,29 +43,36 @@ interface DynamicInvoiceFormProps { onClose: () => void; } -const DynamicInvoiceForm: React.FC = ({ isOpen, onClose }) => { - const { activeTemplateData } = useInvoice(); - const [activeFooterIndex, setActiveFooterIndex] = useState(1); +const DynamicInvoiceForm: React.FC = ({ + isOpen, + onClose, +}) => { + const { activeTemplateData, currentSheetId } = useInvoice(); const [formData, setFormData] = useState({}); const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); - const [toastColor, setToastColor] = useState<"success" | "danger" | "warning">("success"); + const [toastColor, setToastColor] = useState< + "success" | "danger" | "warning" + >("success"); // Get current template data const currentTemplate = useMemo(() => { return activeTemplateData; }, [activeTemplateData]); - // Get active footer based on activeFooterIndex - const activeFooter = useMemo(() => { - return currentTemplate?.footers.find(footer => footer.index === activeFooterIndex); - }, [currentTemplate, activeFooterIndex]); + // Get current sheet ID or fall back to template's current sheet + const effectiveSheetId = useMemo(() => { + return currentSheetId || currentTemplate?.msc?.currentid || "sheet1"; + }, [currentSheetId, currentTemplate]); - // Generate form sections based on cellMappings and active footer + // Generate form sections based on cellMappings and current sheet const formSections = useMemo(() => { if (!currentTemplate) return []; - return DynamicFormManager.getFormSectionsForFooter(currentTemplate, activeFooterIndex); - }, [currentTemplate, activeFooterIndex]); + return DynamicFormManager.getFormSectionsForSheet( + currentTemplate, + effectiveSheetId + ); + }, [currentTemplate, effectiveSheetId]); // Initialize form data when form sections change useEffect(() => { @@ -85,46 +89,61 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose setShowToast(true); }; - const handleFieldChange = (sectionTitle: string, fieldLabel: string, value: string) => { - setFormData(prev => ({ + const handleFieldChange = ( + sectionTitle: string, + fieldLabel: string, + value: string + ) => { + setFormData((prev) => ({ ...prev, [sectionTitle]: { ...prev[sectionTitle], [fieldLabel]: value, - } + }, })); }; - const handleItemChange = (sectionTitle: string, itemIndex: number, fieldName: string, value: string) => { - setFormData(prev => ({ + const handleItemChange = ( + sectionTitle: string, + itemIndex: number, + fieldName: string, + value: string + ) => { + setFormData((prev) => ({ ...prev, - [sectionTitle]: prev[sectionTitle].map((item: any, index: number) => + [sectionTitle]: prev[sectionTitle].map((item: any, index: number) => index === itemIndex ? { ...item, [fieldName]: value } : item - ) + ), })); }; const handleSave = async () => { try { // Validate form data - const validation = DynamicFormManager.validateFormData(formData, formSections); + const validation = DynamicFormManager.validateFormData( + formData, + formSections + ); if (!validation.isValid) { - showToastMessage(`Validation errors: ${validation.errors.join(', ')}`, "warning"); + showToastMessage( + `Validation errors: ${validation.errors.join(", ")}`, + "warning" + ); return; } // Convert form data to spreadsheet format - const cellData = DynamicFormManager.convertToSpreadsheetFormat(formData, formSections, activeFooterIndex); - - // Create invoice data object - const invoiceData = { - templateId: activeTemplateData ? activeTemplateData.templateId : 1, - footerIndex: activeFooterIndex, - cellData, - dynamicData: formData, - }; - - await addInvoiceData(invoiceData); + const cellData = DynamicFormManager.convertToSpreadsheetFormat( + formData, + formSections, + effectiveSheetId + ); + + console.log("Cell data to be saved:", cellData); + console.log("Effective sheet ID:", effectiveSheetId); + + // Use the new addDynamicInvoiceData function that handles cell references + await addDynamicInvoiceData(cellData, effectiveSheetId); showToastMessage("Invoice data saved successfully!", "success"); // Close modal after a short delay @@ -132,6 +151,7 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose onClose(); }, 1500); } catch (error) { + console.error("Error saving invoice data:", error); setToastMessage("Failed to save invoice data. Please try again."); setToastColor("danger"); setShowToast(true); @@ -140,57 +160,72 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose const handleClear = async () => { try { - await clearInvoiceData(); + // Get current cell data to know which cells to clear + const cellData = DynamicFormManager.convertToSpreadsheetFormat( + formData, + formSections, + effectiveSheetId + ); + + await clearDynamicInvoiceData(cellData); + // Reset form data const initData = DynamicFormManager.initializeFormData(formSections); setFormData(initData); showToastMessage("Form data cleared successfully!", "success"); } catch (error) { + console.error("Error clearing form data:", error); showToastMessage("Failed to clear form data", "danger"); } }; const renderField = (field: DynamicFormField, sectionTitle: string) => { const value = formData[sectionTitle]?.[field.label] || ""; - + switch (field.type) { - case 'textarea': + case "textarea": return ( {field.label} handleFieldChange(sectionTitle, field.label, e.detail.value!)} + onIonInput={(e) => + handleFieldChange(sectionTitle, field.label, e.detail.value!) + } placeholder={`Enter ${field.label.toLowerCase()}`} rows={3} /> ); - case 'email': + case "email": return ( {field.label} handleFieldChange(sectionTitle, field.label, e.detail.value!)} + onIonInput={(e) => + handleFieldChange(sectionTitle, field.label, e.detail.value!) + } placeholder={`Enter ${field.label.toLowerCase()}`} /> ); - case 'number': + case "number": return ( {field.label} handleFieldChange(sectionTitle, field.label, e.detail.value!)} + onIonInput={(e) => + handleFieldChange(sectionTitle, field.label, e.detail.value!) + } placeholder={`Enter ${field.label.toLowerCase()}`} /> ); - case 'decimal': + case "decimal": return ( {field.label} @@ -198,7 +233,9 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose type="number" step="0.01" value={value} - onIonInput={(e) => handleFieldChange(sectionTitle, field.label, e.detail.value!)} + onIonInput={(e) => + handleFieldChange(sectionTitle, field.label, e.detail.value!) + } placeholder={`Enter ${field.label.toLowerCase()}`} /> @@ -209,7 +246,9 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose {field.label} handleFieldChange(sectionTitle, field.label, e.detail.value!)} + onIonInput={(e) => + handleFieldChange(sectionTitle, field.label, e.detail.value!) + } placeholder={`Enter ${field.label.toLowerCase()}`} /> @@ -233,18 +272,35 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose Item {index + 1} - {Object.entries(section.itemsConfig!.content).map(([fieldName, cellColumn]) => ( - - {fieldName} - handleItemChange(section.title, index, fieldName, e.detail.value!)} - placeholder={`Enter ${fieldName.toLowerCase()}`} - /> - - ))} + {Object.entries(section.itemsConfig!.content).map( + ([fieldName, cellColumn]) => ( + + {fieldName} + + handleItemChange( + section.title, + index, + fieldName, + e.detail.value! + ) + } + placeholder={`Enter ${fieldName.toLowerCase()}`} + /> + + ) + )}
))} @@ -264,7 +320,7 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose - {section.fields.map(field => renderField(field, section.title))} + {section.fields.map((field) => renderField(field, section.title))} @@ -285,7 +341,7 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose
-
+

No template found for the current selection.

@@ -318,42 +374,27 @@ const DynamicInvoiceForm: React.FC = ({ isOpen, onClose - {/* Footer Selection */} - {currentTemplate.footers.length > 1 && ( + {/* Sheet Information Display */} + {effectiveSheetId && ( - Select Footer + Current Sheet: {effectiveSheetId} - - - Active Footer - setActiveFooterIndex(e.detail.value)} - > - {currentTemplate.footers.map(footer => ( - - {footer.name} - - ))} - - - )} {/* Dynamic Form Sections */} - {formSections.map(section => renderSection(section))} + {formSections.map((section) => renderSection(section))} {/* Action Buttons */} -
+
Save Invoice Data diff --git a/src/components/FileMenu/FileOptions.tsx b/src/components/FileMenu/FileOptions.tsx index 1127642..6daaa04 100644 --- a/src/components/FileMenu/FileOptions.tsx +++ b/src/components/FileMenu/FileOptions.tsx @@ -56,6 +56,7 @@ import { isQuotaExceededError, getQuotaExceededMessage, } from "../../utils/helper.js"; +import TemplateModal from "../TemplateModal/TemplateModal"; interface FileOptionsProps { showActionsPopover: boolean; @@ -64,6 +65,7 @@ interface FileOptionsProps { setShowColorPicker: (show: boolean) => void; onSave?: () => Promise; isAutoSaveEnabled?: boolean; + fileName: string; } const FileOptions: React.FC = ({ @@ -73,11 +75,11 @@ const FileOptions: React.FC = ({ setShowColorPicker, onSave, isAutoSaveEnabled = false, + fileName, }) => { const { isDarkMode } = useTheme(); const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); - const [showUnsavedChangesAlert, setShowUnsavedChangesAlert] = useState(false); const [showSaveAsAlert, setShowSaveAsAlert] = useState(false); const [newFileName, setNewFileName] = useState(""); const [showLogoAlert, setShowLogoAlert] = useState(false); @@ -97,6 +99,9 @@ const FileOptions: React.FC = ({ Array<{ id: string; name: string; data: string }> >([]); + // Template modal state + const [showTemplateModal, setShowTemplateModal] = useState(false); + const { selectedFile, store, @@ -237,30 +242,59 @@ const FileOptions: React.FC = ({ const doSaveAs = async (filename: string) => { try { - if (_validateName(filename)) { - // Check if file already exists - const exists = await _checkForExistingFile(filename); - if (exists) return; - - setToastMessage("Saving file..."); - setShowToast(true); + // Validate the filename first + if (!_validateName(filename)) { + return; + } - const content = AppGeneral.getSpreadsheetContent(); - const now = new Date().toISOString(); + // Check if file already exists + const exists = await _checkForExistingFile(filename); + if (exists) return; - const file = new File( - now, - now, - encodeURIComponent(content), - filename, - 1 - ); - await store._saveFile(file); + setToastMessage("Saving file..."); + setShowToast(true); - setToastMessage("File saved successfully!"); + // Get current file data to copy its structure + const currentFile = await store._getFile(fileName || selectedFile); + const content = AppGeneral.getSpreadsheetContent(); + const now = new Date().toISOString(); + if (!currentFile) { + console.log("No current file found"); + setToastMessage("Error saving file!"); setShowToast(true); - updateSelectedFile(filename); + return; } + // Create new file with all structure from current file + let data = { + created: currentFile?.created || now, + modified: now, + content: encodeURIComponent(content), + name: filename, + billType: currentFile?.billType || billType || 1, + isEncrypted: currentFile?.isEncrypted || false, + templateId: currentFile?.templateId, + }; + + const file = new File( + data.created, + data.modified, + data.content, + data.name, + data.billType, + data.templateId, + data.isEncrypted + ); + console.log(file); + await store._saveFile(file); + + setToastMessage("File saved successfully!"); + setShowToast(true); + // Redirect to the new file after a short delay + setTimeout(() => { + const link = document.createElement("a"); + link.href = `/app/editor/${filename}`; + link.click(); + }, 200); } catch (error) { console.error("Error saving file:", error); @@ -279,80 +313,9 @@ const FileOptions: React.FC = ({ setShowSaveAsAlert(true); }; - const handleNewFileClick = async () => { - try { - setShowActionsPopover(false); - - // Get the default file from storage - const defaultExists = await store._checkKey("default"); - if (selectedFile === "default" && defaultExists) { - const storedDefaultFile = await store._getFile("default"); - - // Decode the stored content - const storedContent = decodeURIComponent(storedDefaultFile.content); - const msc = DATA["home"]["App"]["msc"]; - - const hasUnsavedChanges = storedContent !== JSON.stringify(msc); - - if (hasUnsavedChanges) { - // If there are unsaved changes, show confirmation alert - setShowUnsavedChangesAlert(true); - return; - } - } - await createNewFile(); - } catch (error) { - console.error("Error checking for unsaved changes:", error); - // On error, proceed with normal flow - setShowUnsavedChangesAlert(true); - } - }; - - const createNewFile = async () => { - try { - // Reset to defaults first - resetToDefaults(); - - // Set selected file to "default" - updateSelectedFile("default"); - - const msc = DATA["home"]["App"]["msc"]; - - // Load the template data into the spreadsheet - AppGeneral.viewFile("default", JSON.stringify(msc)); - - // Save the new template as the default file in storage - const templateContent = encodeURIComponent(JSON.stringify(msc)); - const now = new Date().toISOString(); - const newDefaultFile = new File(now, now, templateContent, "default", 1); - await store._saveFile(newDefaultFile); - - setToastMessage("New file created successfully"); - setShowToast(true); - } catch (error) { - console.error("Error creating new file:", error); - - // Check if the error is due to storage quota exceeded - if (isQuotaExceededError(error)) { - setToastMessage(getQuotaExceededMessage("create")); - } else { - setToastMessage("Error creating new invoice"); - } - setShowToast(true); - } - }; - - const handleDiscardAndCreateNew = async () => { - try { - // User confirmed to discard changes, proceed with creating new file - await createNewFile(); - setShowUnsavedChangesAlert(false); - } catch (error) { - console.error("Error discarding and creating new file:", error); - setToastMessage("Error creating new invoice"); - setShowToast(true); - setShowUnsavedChangesAlert(false); - } + const handleNewFileClick = () => { + setShowActionsPopover(false); + setShowTemplateModal(true); }; const getCurrentSelectedCell = (): string | null => { @@ -559,29 +522,6 @@ const FileOptions: React.FC = ({ - {/* Unsaved Changes Confirmation Alert */} - setShowUnsavedChangesAlert(false)} - header="⚠️ Unsaved Changes" - message="The default file has unsaved changes. Creating a new file will discard these changes. Do you want to continue?" - buttons={[ - { - text: "Cancel", - role: "cancel", - handler: () => { - setShowUnsavedChangesAlert(false); - }, - }, - { - text: "Discard & Create New", - handler: async () => { - await handleDiscardAndCreateNew(); - }, - }, - ]} - /> - {/* Save As Alert */} = ({ + + {/* Template Modal */} + setShowTemplateModal(false)} + onFileCreated={(fileName, templateId) => { + setToastMessage(`File "${fileName}" created successfully!`); + setShowToast(true); + }} + /> ); }; diff --git a/src/components/Files/Files.tsx b/src/components/Files/Files.tsx index 539c79c..1b0befa 100644 --- a/src/components/Files/Files.tsx +++ b/src/components/Files/Files.tsx @@ -447,7 +447,12 @@ const Files: React.FC<{ dateCreated: createdDate, dateModified: modifiedDate, type: "local", - templateMetadata: fileData.templateMetadata || null, + templateMetadata: fileData.templateId + ? { + templateId: fileData.templateId, + template: getTemplateInfo(fileData.templateId), + } + : null, }; }); diff --git a/src/components/TemplateModal/TemplateModal.tsx b/src/components/TemplateModal/TemplateModal.tsx new file mode 100644 index 0000000..ef3b6ca --- /dev/null +++ b/src/components/TemplateModal/TemplateModal.tsx @@ -0,0 +1,769 @@ +import React, { useState, useEffect } from "react"; +import { + IonAlert, + IonContent, + IonHeader, + IonModal, + IonTitle, + IonToolbar, + IonButton, + IonIcon, + IonButtons, + IonSegment, + IonSegmentButton, + IonText, + IonToast, +} from "@ionic/react"; +import { + chevronForward, + layers, + close, + phonePortraitOutline, + tabletPortraitOutline, + desktopOutline, + filterOutline, +} from "ionicons/icons"; +import { useTheme } from "../../contexts/ThemeContext"; +import { useInvoice } from "../../contexts/InvoiceContext"; +import { DATA } from "../../templates"; +import { tempMeta } from "../../templates-meta"; +import { File } from "../Storage/LocalStorage"; +import { useHistory } from "react-router-dom"; + +interface TemplateModalProps { + isOpen: boolean; + onClose: () => void; + onFileCreated?: (fileName: string, templateId: number) => void; +} + +const TemplateModal: React.FC = ({ + isOpen, + onClose, + onFileCreated, +}) => { + const { isDarkMode } = useTheme(); + const { store, updateSelectedFile, updateBillType } = useInvoice(); + const history = useHistory(); + + const [showToast, setShowToast] = useState(false); + const [toastMessage, setToastMessage] = useState(""); + const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); + const [selectedTemplateForFile, setSelectedTemplateForFile] = useState< + number | null + >(null); + const [newFileName, setNewFileName] = useState(""); + const [templateFilter, setTemplateFilter] = useState< + "all" | "web" | "mobile" | "tablet" + >("all"); + const [isSmallScreen, setIsSmallScreen] = useState(false); + + // Check screen size + useEffect(() => { + const checkScreenSize = () => { + setIsSmallScreen(window.innerWidth < 692); + }; + + checkScreenSize(); + window.addEventListener("resize", checkScreenSize); + return () => window.removeEventListener("resize", checkScreenSize); + }, []); + + const getTemplateMetadata = (templateId: number) => { + return tempMeta.find((meta) => meta.template_id === templateId); + }; + + // Categorize templates based on their names + const categorizeTemplate = (templateName: string | undefined) => { + if (!templateName) return "web"; + const name = templateName.toLowerCase(); + if (name.includes("mobile")) { + return "mobile"; + } else if (name.includes("tablet")) { + return "tablet"; + } else { + return "web"; + } + }; + + // Get categorized templates + const getCategorizedTemplates = () => { + const templates = tempMeta; + const categorized = { + web: templates.filter((t) => { + const metadata = getTemplateMetadata(t.template_id); + const templateName = metadata?.name || t.name || "Unknown Template"; + return categorizeTemplate(templateName) === "web"; + }), + mobile: templates.filter((t) => { + const metadata = getTemplateMetadata(t.template_id); + const templateName = metadata?.name || t.name || "Unknown Template"; + return categorizeTemplate(templateName) === "mobile"; + }), + tablet: templates.filter((t) => { + const metadata = getTemplateMetadata(t.template_id); + const templateName = metadata?.name || t.name || "Unknown Template"; + return categorizeTemplate(templateName) === "tablet"; + }), + }; + return categorized; + }; + + // Get filtered templates based on current filter + const getFilteredTemplates = () => { + const categorized = getCategorizedTemplates(); + + if (templateFilter === "all") { + // Return in order: web, mobile, tablet + return [...categorized.web, ...categorized.mobile, ...categorized.tablet]; + } else { + return categorized[templateFilter] || []; + } + }; + + const handleTemplateSelect = (templateId: number) => { + setSelectedTemplateForFile(templateId); + setShowFileNamePrompt(true); + }; + + // Reset template filter when modal closes + const handleModalClose = () => { + setTemplateFilter("all"); + setSelectedTemplateForFile(null); + setNewFileName(""); + setShowFileNamePrompt(false); + onClose(); + }; + + /* Utility functions */ + const _validateName = async (filename: string) => { + filename = filename.trim(); + if (filename === "Untitled") { + return { + isValid: false, + message: "cannot update Untitled file! Use Save As Button to save.", + }; + } else if (filename === "" || !filename) { + return { + isValid: false, + message: "Filename cannot be empty", + }; + } else if (filename.length > 30) { + return { + isValid: false, + message: "Filename too long", + }; + } else if (/^[a-zA-Z0-9- ]*$/.test(filename) === false) { + return { + isValid: false, + message: "Special Characters cannot be used", + }; + } else if (await store._checkKey(filename)) { + return { + isValid: false, + message: "Filename already exists", + }; + } + return { + isValid: true, + message: "", + }; + }; + + // Create new file with template + const createNewFileWithTemplate = async ( + templateId: number, + fileName: string + ) => { + try { + // Validate filename first + const validation = await _validateName(fileName); + if (!validation.isValid) { + setToastMessage(validation.message); + setShowToast(true); + return; + } + + const templateData = DATA[templateId]; + if (!templateData) { + setToastMessage("Template not found"); + setShowToast(true); + return; + } + + const mscContent = templateData.msc; + const jsonMsc = JSON.stringify(mscContent); + if (!mscContent) { + setToastMessage("Error creating template content"); + setShowToast(true); + return; + } + + // Find the active footer index, default to 1 if none found + const activeFooter = templateData.footers?.find( + (footer) => footer.isActive + ); + const activeFooterIndex = activeFooter ? activeFooter.index : 1; + + const now = new Date().toISOString(); + const newFile = new File( + now, + now, + encodeURIComponent(jsonMsc), // mscContent is already a JSON string + fileName, + activeFooterIndex, + templateId, + false + ); + + await store._saveFile(newFile); + + setToastMessage( + `File "${fileName}" created with ${templateData.template}` + ); + setShowToast(true); + + // Reset modal state + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + setNewFileName(""); + handleModalClose(); + + updateSelectedFile(fileName); + updateBillType(activeFooterIndex); + + // Call the callback if provided + if (onFileCreated) { + onFileCreated(fileName, templateId); + } + + setTimeout(() => { + const link = document.createElement("a"); + link.href = `/app/editor/${fileName}`; + link.click(); + }, 200); + } catch (error) { + setToastMessage("Failed to create file"); + setShowToast(true); + } + }; + + // Helper function to render individual template items + const renderTemplateItem = (template: any, keyPrefix?: string) => { + const metadata = getTemplateMetadata( + template.templateId || template.template_id + ); + const templateName = + metadata?.name || + template.template || + template.name || + "Unknown Template"; + const category = categorizeTemplate(templateName); + + // Get the template data from DATA to access footers + const templateData = DATA[template.templateId || template.template_id]; + const footers = templateData?.footers || []; + + return ( +
+ handleTemplateSelect(template.templateId || template.template_id) + } + style={{ + border: `1px solid ${ + isDarkMode + ? "var(--ion-color-step-200)" + : "var(--ion-color-step-150)" + }`, + borderRadius: "8px", + padding: "12px", + marginBottom: "12px", + cursor: "pointer", + backgroundColor: isDarkMode + ? "var(--ion-color-step-50)" + : "var(--ion-background-color)", + display: "flex", + alignItems: "center", + gap: "12px", + transition: "all 0.2s ease", + }} + onMouseOver={(e) => { + e.currentTarget.style.backgroundColor = isDarkMode + ? "var(--ion-color-step-100)" + : "var(--ion-color-step-50)"; + e.currentTarget.style.borderColor = isDarkMode + ? "var(--ion-color-step-300)" + : "var(--ion-color-step-200)"; + }} + onMouseOut={(e) => { + e.currentTarget.style.backgroundColor = isDarkMode + ? "var(--ion-color-step-50)" + : "var(--ion-background-color)"; + e.currentTarget.style.borderColor = isDarkMode + ? "var(--ion-color-step-200)" + : "var(--ion-color-step-150)"; + }} + > + {/* Template Image */} +
+ {metadata?.ImageUri ? ( + {metadata.name} + ) : ( + + )} +
+ + {/* Template Info */} +
+

+ {templateName} +

+

+ {footers.length} footer{footers.length !== 1 ? "s" : ""} +

+ {/* Category Badge */} +
+ {category} +
+
+ + {/* Arrow Icon */} + +
+ ); + }; + + const filteredTemplates = getFilteredTemplates(); + const categorized = getCategorizedTemplates(); + + return ( + <> + + + + Choose Template + + + + + + + + + {/* Filter Segment */} +
+ + setTemplateFilter( + e.detail.value as "all" | "web" | "mobile" | "tablet" + ) + } + style={{ + background: isDarkMode + ? "var(--ion-color-step-150)" + : "var(--ion-background-color)", + borderRadius: "8px", + padding: "3px", + border: `1px solid ${ + isDarkMode + ? "var(--ion-color-step-250)" + : "var(--ion-color-step-150)" + }`, + boxShadow: "none", + "--background": isDarkMode + ? "var(--ion-color-step-150)" + : "var(--ion-background-color)", + "--background-checked": isDarkMode + ? "var(--ion-color-primary)" + : "var(--ion-color-primary)", + "--color": isDarkMode ? "#ffffff" : "#000000", + "--color-checked": "#ffffff", + }} + > + + + + All ( + {categorized.web.length + + categorized.mobile.length + + categorized.tablet.length} + ) + + + + + + Web ({categorized.web.length}) + + + + + + Mobile ({categorized.mobile.length}) + + + + + + Tablet ({categorized.tablet.length}) + + + +
+ +
+ {filteredTemplates.length === 0 ? ( + +

+ No templates found in this category. +

+
+ ) : ( +
+ {filteredTemplates.map((template) => + renderTemplateItem(template, "template-modal") + )} +
+ )} +
+
+
+ + {/* File Name Prompt Alert */} + {showFileNamePrompt && + selectedTemplateForFile !== null && + getTemplateMetadata(selectedTemplateForFile) && ( + { + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + setNewFileName(""); + }} + header="Create New File" + message={`Create a new ${ + getTemplateMetadata(selectedTemplateForFile)?.name + } file`} + inputs={[ + { + name: "filename", + type: "text", + value: newFileName, + placeholder: "Enter file name", + }, + ]} + buttons={[ + { + text: "Cancel", + role: "cancel", + handler: () => { + setSelectedTemplateForFile(null); + setNewFileName(""); + }, + }, + { + text: "Create", + handler: async (data) => { + const fileName = data.filename?.trim(); + if (!fileName) { + setToastMessage("Please enter a file name"); + setShowToast(true); + // Clear the filename and close the alert when validation fails + setNewFileName(""); + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + return false; // Prevent alert from closing automatically + } + + if (selectedTemplateForFile) { + // Validate the filename before creating + const validation = await _validateName(fileName); + if (!validation.isValid) { + setToastMessage(validation.message); + setShowToast(true); + // Clear the filename and close the alert when validation fails + setNewFileName(""); + setShowFileNamePrompt(false); + setSelectedTemplateForFile(null); + return false; // Prevent alert from closing automatically + } + + // If validation passes, create the file + await createNewFileWithTemplate( + selectedTemplateForFile, + fileName + ); + return true; // Allow alert to close + } + return false; + }, + }, + ]} + /> + )} + + {/* Toast for notifications */} + setShowToast(false)} + message={toastMessage} + duration={3000} + color={toastMessage.includes("successfully") ? "success" : "warning"} + position="top" + /> + + ); +}; + +export default TemplateModal; diff --git a/src/components/socialcalc/modules/invoice.js b/src/components/socialcalc/modules/invoice.js index c10fdae..ea09201 100644 --- a/src/components/socialcalc/modules/invoice.js +++ b/src/components/socialcalc/modules/invoice.js @@ -218,6 +218,189 @@ export function addInvoiceData(invoiceData) { }); } +export function addDynamicInvoiceData(cellData, sheetId) { + return new Promise(function (resolve, reject) { + console.log("=== ADD DYNAMIC INVOICE DATA START ==="); + console.log("Cell data:", cellData); + console.log("Sheet ID:", sheetId); + + try { + var control = SocialCalc.GetCurrentWorkBookControl(); + console.log("Workbook control:", control ? "Found" : "Not found"); + + if (!control) { + throw new Error("No workbook control available"); + } + + if (!control.currentSheetButton) { + throw new Error("No current sheet button available"); + } + + var currsheet = control.currentSheetButton.id; + console.log("Current active sheet:", currsheet); + + // Build commands to set all values from cellData + var commands = []; + + // Iterate through cellData and create set commands + Object.entries(cellData).forEach(([cellRef, value]) => { + if (value !== undefined && value !== null && value !== "") { + // Determine if the value is numeric or text + const stringValue = value.toString().trim(); + const numericValue = parseFloat(stringValue); + + if ( + !isNaN(numericValue) && + isFinite(numericValue) && + stringValue === numericValue.toString() + ) { + // It's a valid number + commands.push(`set ${cellRef} value n ${numericValue}`); + } else { + // It's text - encode it properly for SocialCalc + const encodedValue = SocialCalc.encodeForSave + ? SocialCalc.encodeForSave(stringValue) + : stringValue; + commands.push(`set ${cellRef} text t ${encodedValue}`); + } + console.log(`Setting cell ${cellRef} = ${value}`); + } + }); + + if (commands.length === 0) { + console.log("No data to update"); + resolve(true); + return; + } + + var cmd = commands.join("\n") + "\n"; + console.log("Generated SocialCalc commands:", cmd); + + var commandObj = { + cmdtype: "scmd", + id: currsheet, + cmdstr: cmd, + saveundo: false, + }; + + console.log("Command object:", commandObj); + + try { + control.ExecuteWorkBookControlCommand(commandObj, false); + console.log("✓ Dynamic invoice data added successfully"); + console.log("=== ADD DYNAMIC INVOICE DATA SUCCESS ==="); + resolve(true); + } catch (execError) { + console.error("Error executing command:", execError); + throw execError; + } + } catch (error) { + console.error("=== ADD DYNAMIC INVOICE DATA ERROR ==="); + console.error("Error details:", error); + console.error("Stack trace:", error.stack); + reject(error); + } + }); +} + +export function clearDynamicInvoiceData(cellData) { + return new Promise(function (resolve, reject) { + console.log("=== CLEAR DYNAMIC INVOICE DATA START ==="); + console.log("Cell data to clear:", cellData); + + try { + var control = SocialCalc.GetCurrentWorkBookControl(); + console.log("Workbook control:", control ? "Found" : "Not found"); + + if (!control) { + throw new Error("No workbook control available"); + } + + if (!control.currentSheetButton) { + throw new Error("No current sheet button available"); + } + + var currsheet = control.currentSheetButton.id; + console.log("Current active sheet:", currsheet); + + // Build commands to clear all values from cellData + var commands = []; + + // If cellData is provided, clear only those specific cells + if (cellData && Object.keys(cellData).length > 0) { + Object.keys(cellData).forEach((cellRef) => { + commands.push(`erase ${cellRef} formulas`); + }); + } else { + // Fallback to clearing predefined coordinates + const coordinates = getInvoiceCoordinates(); + + // Clear Bill To information + commands.push(`erase ${coordinates.billTo.name} formulas`); + commands.push(`erase ${coordinates.billTo.streetAddress} formulas`); + commands.push(`erase ${coordinates.billTo.cityStateZip} formulas`); + commands.push(`erase ${coordinates.billTo.phone} formulas`); + commands.push(`erase ${coordinates.billTo.email} formulas`); + + // Clear From information + commands.push(`erase ${coordinates.from.name} formulas`); + commands.push(`erase ${coordinates.from.streetAddress} formulas`); + commands.push(`erase ${coordinates.from.cityStateZip} formulas`); + commands.push(`erase ${coordinates.from.phone} formulas`); + commands.push(`erase ${coordinates.from.email} formulas`); + + // Clear Invoice information + commands.push(`erase ${coordinates.invoice.number} formulas`); + commands.push(`erase ${coordinates.invoice.date} formulas`); + + // Clear all items + for ( + let row = coordinates.items.startRow; + row <= coordinates.items.endRow; + row++ + ) { + commands.push( + `erase ${coordinates.items.descriptionColumn}${row} formulas` + ); + commands.push( + `erase ${coordinates.items.amountColumn}${row} formulas` + ); + } + + // Clear total + commands.push(`erase ${coordinates.total.sum} formulas`); + } + + var cmd = commands.join("\n") + "\n"; + console.log("Generated SocialCalc clear commands:", cmd); + + var commandObj = { + cmdtype: "scmd", + id: currsheet, + cmdstr: cmd, + saveundo: false, + }; + + console.log("Command object:", commandObj); + + try { + control.ExecuteWorkBookControlCommand(commandObj, false); + console.log("✓ Dynamic invoice data cleared successfully"); + console.log("=== CLEAR DYNAMIC INVOICE DATA SUCCESS ==="); + resolve(true); + } catch (execError) { + console.error("Error executing command:", execError); + throw execError; + } + } catch (error) { + console.error("=== CLEAR DYNAMIC INVOICE DATA ERROR ==="); + console.error("Error details:", error); + console.error("Stack trace:", error.stack); + reject(error); + } + }); +} + export function clearInvoiceData() { return new Promise(function (resolve, reject) { console.log("=== CLEAR INVOICE DATA START ==="); diff --git a/src/contexts/InvoiceContext.tsx b/src/contexts/InvoiceContext.tsx index ce6e413..17e5e77 100644 --- a/src/contexts/InvoiceContext.tsx +++ b/src/contexts/InvoiceContext.tsx @@ -13,9 +13,11 @@ interface InvoiceContextType { billType: number; store: Local; activeTemplateData: TemplateData | null; + currentSheetId: string | null; updateSelectedFile: (fileName: string) => void; updateBillType: (type: number) => void; updateActiveTemplateData: (templateData: TemplateData | null) => void; + updateCurrentSheetId: (sheetId: string) => void; resetToDefaults: () => void; } @@ -38,7 +40,9 @@ export const InvoiceProvider: React.FC = ({ }) => { const [selectedFile, setSelectedFile] = useState("file_not_found"); const [billType, setBillType] = useState(1); - const [activeTemplateData, setActiveTemplateData] = useState(null); + const [activeTemplateData, setActiveTemplateData] = + useState(null); + const [currentSheetId, setCurrentSheetId] = useState(null); const [store] = useState(() => new Local()); // Load persisted state from localStorage on mount @@ -46,7 +50,12 @@ export const InvoiceProvider: React.FC = ({ try { const savedFile = localStorage.getItem("stark-invoice-selected-file"); const savedBillType = localStorage.getItem("stark-invoice-bill-type"); - const savedActiveTemplateId = localStorage.getItem("stark-invoice-active-template-id"); + const savedActiveTemplateId = localStorage.getItem( + "stark-invoice-active-template-id" + ); + const savedCurrentSheetId = localStorage.getItem( + "stark-invoice-current-sheet-id" + ); if (savedFile) { setSelectedFile(savedFile); @@ -61,8 +70,16 @@ export const InvoiceProvider: React.FC = ({ const templateData = DATA[templateId]; if (templateData) { setActiveTemplateData(templateData); + // Set current sheet ID from template data if not saved separately + if (!savedCurrentSheetId && templateData.msc.currentid) { + setCurrentSheetId(templateData.msc.currentid); + } } } + + if (savedCurrentSheetId) { + setCurrentSheetId(savedCurrentSheetId); + } } catch (error) { // Failed to load invoice state from localStorage } @@ -88,7 +105,10 @@ export const InvoiceProvider: React.FC = ({ useEffect(() => { try { if (activeTemplateData) { - localStorage.setItem("stark-invoice-active-template-id", activeTemplateData.templateId.toString()); + localStorage.setItem( + "stark-invoice-active-template-id", + activeTemplateData.templateId.toString() + ); } else { localStorage.removeItem("stark-invoice-active-template-id"); } @@ -97,6 +117,18 @@ export const InvoiceProvider: React.FC = ({ } }, [activeTemplateData]); + useEffect(() => { + try { + if (currentSheetId) { + localStorage.setItem("stark-invoice-current-sheet-id", currentSheetId); + } else { + localStorage.removeItem("stark-invoice-current-sheet-id"); + } + } catch (error) { + // Failed to save current sheet id to localStorage + } + }, [currentSheetId]); + const updateSelectedFile = (fileName: string) => { setSelectedFile(fileName); }; @@ -107,12 +139,21 @@ export const InvoiceProvider: React.FC = ({ const updateActiveTemplateData = (templateData: TemplateData | null) => { setActiveTemplateData(templateData); + // Automatically update current sheet ID when template changes + if (templateData && templateData.msc.currentid) { + setCurrentSheetId(templateData.msc.currentid); + } + }; + + const updateCurrentSheetId = (sheetId: string) => { + setCurrentSheetId(sheetId); }; const resetToDefaults = () => { setSelectedFile("File_Not_found"); setBillType(1); setActiveTemplateData(null); + setCurrentSheetId(null); }; const value: InvoiceContextType = { @@ -120,9 +161,11 @@ export const InvoiceProvider: React.FC = ({ billType, store, activeTemplateData, + currentSheetId, updateSelectedFile, updateBillType, updateActiveTemplateData, + updateCurrentSheetId, resetToDefaults, }; diff --git a/src/pages/FilesPage.tsx b/src/pages/FilesPage.tsx index 2f452cb..607b5c8 100644 --- a/src/pages/FilesPage.tsx +++ b/src/pages/FilesPage.tsx @@ -37,6 +37,7 @@ import * as AppGeneral from "../components/socialcalc/index"; import "./FilesPage.css"; import { useHistory } from "react-router-dom"; import { File } from "../components/Storage/LocalStorage"; +import TemplateModal from "../components/TemplateModal/TemplateModal"; const FilesPage: React.FC = () => { const { isDarkMode, toggleDarkMode } = useTheme(); const { selectedFile, store, updateSelectedFile, updateBillType } = @@ -45,16 +46,19 @@ const FilesPage: React.FC = () => { const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(""); - const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); - const [selectedTemplateForFile, setSelectedTemplateForFile] = useState< - number | null - >(null); - const [newFileName, setNewFileName] = useState(""); const [showTemplateModal, setShowTemplateModal] = useState(false); + const [showSharedTemplateModal, setShowSharedTemplateModal] = useState(false); const [isSmallScreen, setIsSmallScreen] = useState(false); + + // Legacy states for old template modal (will be removed later) const [templateFilter, setTemplateFilter] = useState< "all" | "web" | "mobile" | "tablet" >("all"); + const [selectedTemplateForFile, setSelectedTemplateForFile] = useState< + number | null + >(null); + const [showFileNamePrompt, setShowFileNamePrompt] = useState(false); + const [newFileName, setNewFileName] = useState(""); const [device] = useState(AppGeneral.getDeviceType()); @@ -694,9 +698,7 @@ const FilesPage: React.FC = () => { ? `${keyPrefix}-${template.templateId || template.template_id}` : template.templateId || template.template_id } - onClick={() => - handleTemplateSelect(template.templateId || template.template_id) - } + onClick={() => setShowSharedTemplateModal(true)} style={{ border: `1px solid ${ isDarkMode @@ -941,9 +943,7 @@ const FilesPage: React.FC = () => { return (
- handleTemplateSelect(template.templateId) - } + onClick={() => setShowSharedTemplateModal(true)} style={{ border: "2px solid var(--ion-color-light)", borderRadius: "12px", @@ -1045,7 +1045,7 @@ const FilesPage: React.FC = () => { {/* Plus icon card to show more templates */}
setShowTemplateModal(true)} + onClick={() => setShowSharedTemplateModal(true)} style={{ border: "2px dashed var(--ion-color-light)", borderRadius: "12px", @@ -1153,7 +1153,7 @@ const FilesPage: React.FC = () => { return (
handleTemplateSelect(template.templateId)} + onClick={() => setShowSharedTemplateModal(true)} style={{ minWidth: "110px", width: "110px", @@ -1245,7 +1245,7 @@ const FilesPage: React.FC = () => { {/* Plus icon card to show more templates */}
setShowTemplateModal(true)} + onClick={() => setShowSharedTemplateModal(true)} style={{ minWidth: "110px", width: "110px", @@ -1318,8 +1318,15 @@ const FilesPage: React.FC = () => { updateBillType={updateBillType} /> - {/* Template Modal for small screens */} - {renderTemplateModal()} + {/* Template Modal */} + setShowSharedTemplateModal(false)} + onFileCreated={(fileName, templateId) => { + setToastMessage(`File "${fileName}" created successfully!`); + setShowToast(true); + }} + /> { color={toastMessage.includes("successfully") ? "success" : "warning"} position="top" /> - - {/* File Name Prompt Alert Wrapper */} - {showFileNamePrompt && - selectedTemplateForFile !== null && - getTemplateMetadata(selectedTemplateForFile) && ( - { - setShowFileNamePrompt(false); - setSelectedTemplateForFile(null); - setNewFileName(""); - }} - header="Create New File" - message={`Create a new ${ - getTemplateMetadata(selectedTemplateForFile)?.name - } file`} - inputs={[ - { - name: "filename", - type: "text", - value: newFileName, - placeholder: "Enter file name", - }, - ]} - buttons={[ - { - text: "Cancel", - role: "cancel", - handler: () => { - setSelectedTemplateForFile(null); - setNewFileName(""); - }, - }, - { - text: "Create", - handler: async (data) => { - const fileName = data.filename?.trim(); - if (!fileName) { - setToastMessage("Please enter a file name"); - setShowToast(true); - // Clear the filename and close the alert when validation fails - setNewFileName(""); - setShowFileNamePrompt(false); - setSelectedTemplateForFile(null); - return false; // Prevent alert from closing automatically - } - - if (selectedTemplateForFile) { - // Validate the filename before creating - const validation = await _validateName(fileName); - if (!validation.isValid) { - setToastMessage(validation.message); - setShowToast(true); - // Clear the filename and close the alert when validation fails - setNewFileName(""); - setShowFileNamePrompt(false); - setSelectedTemplateForFile(null); - return false; // Prevent alert from closing automatically - } - - // If validation passes, create the file - await createNewFileWithTemplate( - selectedTemplateForFile, - fileName - ); - return true; // Allow alert to close - } - return false; - }, - }, - ]} - /> - )} ); }; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 5e589a3..0b85327 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -55,6 +55,7 @@ import { useHistory, useLocation, useParams } from "react-router-dom"; import DynamicInvoiceForm from "../components/DynamicInvoiceForm"; import { isQuotaExceededError, getQuotaExceededMessage } from "../utils/helper"; import { getAutoSaveEnabled } from "../utils/settings"; +import { SheetChangeMonitor } from "../utils/sheetChangeMonitor"; import { backgroundClip } from "html2canvas/dist/types/css/property-descriptors/background-clip"; const Home: React.FC = () => { @@ -67,6 +68,7 @@ const Home: React.FC = () => { updateBillType, activeTemplateData, updateActiveTemplateData, + updateCurrentSheetId, } = useInvoice(); const history = useHistory(); @@ -566,6 +568,21 @@ const Home: React.FC = () => { initializeApp(); }, [fileName]); // Only depend on fileName to prevent loops with fileName updates + // Initialize sheet change monitor + useEffect(() => { + if (fileName && activeTemplateData) { + // Wait a bit for SocialCalc to be fully initialized + const timer = setTimeout(() => { + SheetChangeMonitor.initialize(updateCurrentSheetId); + }, 1000); + + return () => { + clearTimeout(timer); + SheetChangeMonitor.cleanup(); + }; + } + }, [fileName, activeTemplateData, updateCurrentSheetId]); + useEffect(() => { if (fileName) { updateSelectedFile(fileName); @@ -1062,6 +1079,7 @@ const Home: React.FC = () => { setShowColorPicker={setShowColorModal} onSave={handleSave} isAutoSaveEnabled={isAutoSaveEnabled} + fileName={fileName} /> {/* Color Picker Modal */} diff --git a/src/utils/dynamicFormManager.ts b/src/utils/dynamicFormManager.ts index afff0d4..ae0a155 100644 --- a/src/utils/dynamicFormManager.ts +++ b/src/utils/dynamicFormManager.ts @@ -246,13 +246,13 @@ export class DynamicFormManager { * Converts form data to spreadsheet format for cell mapping * @param formData The form data * @param sections The form sections - * @param footerIndex The active footer index + * @param sheetId The current sheet ID (optional, for future use) * @returns Object with cell references and values */ static convertToSpreadsheetFormat( formData: ProcessedFormData, sections: DynamicFormSection[], - footerIndex: number + sheetId?: string | number ): { [cellRef: string]: any } { const cellData: { [cellRef: string]: any } = {}; @@ -298,6 +298,22 @@ export class DynamicFormManager { ); } + /** + * Gets form sections based on current sheet ID instead of footer index + * @param template The template data + * @param sheetId The current sheet ID + * @returns Array of form sections for the specified sheet + */ + static getFormSectionsForSheet( + template: TemplateData, + sheetId: string + ): DynamicFormSection[] { + const cellMappings = template.cellMappings[sheetId]; + if (!cellMappings) return []; + + return this.generateFormSections(cellMappings); + } + /** * Filters form sections based on footer index * @param template The template data diff --git a/src/utils/sheetChangeMonitor.ts b/src/utils/sheetChangeMonitor.ts new file mode 100644 index 0000000..4e6b2ee --- /dev/null +++ b/src/utils/sheetChangeMonitor.ts @@ -0,0 +1,118 @@ +/** + * Utility to monitor sheet changes in SocialCalc and update the React context + */ + +declare global { + interface Window { + SocialCalc: any; + } +} + +export class SheetChangeMonitor { + private static isInitialized = false; + private static updateSheetIdCallback: ((sheetId: string) => void) | null = + null; + private static intervalId: NodeJS.Timeout | null = null; + private static lastKnownSheetId: string | null = null; + + /** + * Initialize the sheet change monitor + * @param updateSheetId Callback to update the current sheet ID in React context + */ + static initialize(updateSheetId: (sheetId: string) => void) { + if (this.isInitialized) { + return; + } + + this.updateSheetIdCallback = updateSheetId; + this.startMonitoring(); + this.isInitialized = true; + } + + /** + * Start monitoring for sheet changes + */ + private static startMonitoring() { + // Poll for sheet changes every 500ms + this.intervalId = setInterval(() => { + this.checkCurrentSheet(); + }, 500); + } + + /** + * Check the current sheet and update if it has changed + */ + private static checkCurrentSheet() { + try { + if (typeof window === "undefined" || !window.SocialCalc) { + return; + } + + const SocialCalc = window.SocialCalc; + const control = + SocialCalc.GetCurrentWorkBookControl && + SocialCalc.GetCurrentWorkBookControl(); + + if (!control || !control.currentSheetButton) { + return; + } + + const currentSheetId = control.currentSheetButton.id; + + // If sheet has changed, update the context + if (currentSheetId !== this.lastKnownSheetId) { + this.lastKnownSheetId = currentSheetId; + if (this.updateSheetIdCallback) { + this.updateSheetIdCallback(currentSheetId); + } + } + } catch (error) { + // Silently handle errors to avoid console spam + } + } + + /** + * Get the current sheet ID directly from SocialCalc + * @returns Current sheet ID or null if not available + */ + static getCurrentSheetId(): string | null { + try { + if (typeof window === "undefined" || !window.SocialCalc) { + return null; + } + + const SocialCalc = window.SocialCalc; + const control = + SocialCalc.GetCurrentWorkBookControl && + SocialCalc.GetCurrentWorkBookControl(); + + if (!control || !control.currentSheetButton) { + return null; + } + + return control.currentSheetButton.id; + } catch (error) { + return null; + } + } + + /** + * Stop monitoring sheet changes + */ + static cleanup() { + if (this.intervalId) { + clearInterval(this.intervalId); + this.intervalId = null; + } + this.isInitialized = false; + this.updateSheetIdCallback = null; + this.lastKnownSheetId = null; + } + + /** + * Force a manual check of the current sheet + */ + static forceCheck() { + this.checkCurrentSheet(); + } +} From 9847cdfb9ca30064b5984021f3f619313fadc407 Mon Sep 17 00:00:00 2001 From: anisharma07 Date: Sun, 31 Aug 2025 19:43:28 +0530 Subject: [PATCH 9/9] changes --- src/components/DynamicInvoiceForm.tsx | 233 +++++++++++++++++-- src/components/socialcalc/modules/invoice.js | 165 +++++++++++-- src/utils/dynamicFormManager.ts | 175 ++++++++++++-- 3 files changed, 523 insertions(+), 50 deletions(-) diff --git a/src/components/DynamicInvoiceForm.tsx b/src/components/DynamicInvoiceForm.tsx index fd2b8f5..bf6e843 100644 --- a/src/components/DynamicInvoiceForm.tsx +++ b/src/components/DynamicInvoiceForm.tsx @@ -22,13 +22,21 @@ import { IonTextarea, IonChip, } from "@ionic/react"; -import { close, save, trash, layers } from "ionicons/icons"; +import { + close, + save, + trash, + layers, + refresh, + add, + remove, +} from "ionicons/icons"; import { useInvoice } from "../contexts/InvoiceContext"; import { addInvoiceData, addDynamicInvoiceData, clearInvoiceData, - clearDynamicInvoiceData, + getDynamicInvoiceData, } from "./socialcalc/modules/invoice.js"; import { DynamicFormManager, @@ -54,6 +62,7 @@ const DynamicInvoiceForm: React.FC = ({ const [toastColor, setToastColor] = useState< "success" | "danger" | "warning" >("success"); + const [lastSyncTime, setLastSyncTime] = useState(0); // Get current template data const currentTemplate = useMemo(() => { @@ -76,10 +85,56 @@ const DynamicInvoiceForm: React.FC = ({ // Initialize form data when form sections change useEffect(() => { - const initData = DynamicFormManager.initializeFormData(formSections); - setFormData(initData); + const loadFormData = async () => { + try { + if (formSections.length === 0) return; + + // Get all cell references from the form sections + const cellReferences = + DynamicFormManager.getAllCellReferences(formSections); + + if (cellReferences.length > 0) { + console.log("Loading existing data from cells:", cellReferences); + + // Get existing data from the spreadsheet + const existingCellData = await getDynamicInvoiceData(cellReferences); + + // Convert cell data back to form data structure + const existingFormData = + DynamicFormManager.convertFromSpreadsheetFormat( + existingCellData, + formSections + ); + + console.log("Loaded existing form data:", existingFormData); + setFormData(existingFormData); + } else { + // No cell references, initialize with empty data + const initData = DynamicFormManager.initializeFormData(formSections); + setFormData(initData); + } + } catch (error) { + console.error("Error loading existing form data:", error); + // Fall back to empty initialization + const initData = DynamicFormManager.initializeFormData(formSections); + setFormData(initData); + } + }; + + loadFormData(); }, [formSections]); + // Auto-refresh data when modal opens + useEffect(() => { + if (isOpen && formSections.length > 0) { + const timeoutId = setTimeout(() => { + handleRefresh(); + }, 200); + + return () => clearTimeout(timeoutId); + } + }, [isOpen, formSections]); + const showToastMessage = ( message: string, color: "success" | "danger" | "warning" = "success" @@ -144,6 +199,10 @@ const DynamicInvoiceForm: React.FC = ({ // Use the new addDynamicInvoiceData function that handles cell references await addDynamicInvoiceData(cellData, effectiveSheetId); + + // Update last sync time to prevent immediate auto-sync + setLastSyncTime(Date.now()); + showToastMessage("Invoice data saved successfully!", "success"); // Close modal after a short delay @@ -158,27 +217,140 @@ const DynamicInvoiceForm: React.FC = ({ } }; - const handleClear = async () => { + const saveToSheet = async () => { try { - // Get current cell data to know which cells to clear + // Validate form data + const validation = DynamicFormManager.validateFormData( + formData, + formSections + ); + if (!validation.isValid) { + console.log("Validation errors during save:", validation.errors); + return; + } + + // Convert form data to spreadsheet format const cellData = DynamicFormManager.convertToSpreadsheetFormat( formData, formSections, effectiveSheetId ); - await clearDynamicInvoiceData(cellData); + console.log("Saving to sheet after item change:", cellData); + + // Use the new addDynamicInvoiceData function that handles cell references + await addDynamicInvoiceData(cellData, effectiveSheetId); + + // Update last sync time to prevent immediate auto-sync + setLastSyncTime(Date.now()); + } catch (error) { + console.error("Error saving to sheet:", error); + } + }; - // Reset form data + const handleClear = async () => { + try { + // Reset form data without affecting the sheet const initData = DynamicFormManager.initializeFormData(formSections); setFormData(initData); - showToastMessage("Form data cleared successfully!", "success"); + showToastMessage( + "Form fields cleared! Click 'Save Data' to apply changes to sheet.", + "success" + ); } catch (error) { console.error("Error clearing form data:", error); showToastMessage("Failed to clear form data", "danger"); } }; + const handleRefresh = async () => { + try { + // Get all cell references from the form sections + const cellReferences = + DynamicFormManager.getAllCellReferences(formSections); + + if (cellReferences.length > 0) { + console.log("Refreshing data from cells:", cellReferences); + + // Get current data from the spreadsheet + const currentCellData = await getDynamicInvoiceData(cellReferences); + + // Convert cell data back to form data structure + const refreshedFormData = + DynamicFormManager.convertFromSpreadsheetFormat( + currentCellData, + formSections + ); + + console.log("Refreshed form data:", refreshedFormData); + setFormData(refreshedFormData); + showToastMessage("Form data refreshed from spreadsheet!", "success"); + } else { + showToastMessage("No data to refresh", "warning"); + } + } catch (error) { + console.error("Error refreshing form data:", error); + showToastMessage("Failed to refresh form data", "danger"); + } + }; + + const handleAddItem = (sectionTitle: string) => { + const section = formSections.find((s) => s.title === sectionTitle); + if (!section || !section.itemsConfig) return; + + setFormData((prev) => { + const currentItems = prev[sectionTitle] as any[]; + const maxItems = + section.itemsConfig!.range.end - section.itemsConfig!.range.start + 1; + + if (currentItems.length >= maxItems) { + showToastMessage(`Maximum ${maxItems} items allowed`, "warning"); + return prev; + } + + const newItem: any = {}; + Object.keys(section.itemsConfig!.content).forEach((contentKey) => { + newItem[contentKey] = ""; + }); + + return { + ...prev, + [sectionTitle]: [...currentItems, newItem], + }; + }); + + // Trigger save after state update (but don't close modal) + setTimeout(() => { + saveToSheet(); + }, 100); + }; + + const handleRemoveItem = (sectionTitle: string, itemIndex: number) => { + setFormData((prev) => { + const currentItems = prev[sectionTitle] as any[]; + + if (currentItems.length <= 1) { + showToastMessage("At least one item is required", "warning"); + return prev; + } + + const updatedItems = currentItems.filter( + (_, index) => index !== itemIndex + ); + const newFormData = { + ...prev, + [sectionTitle]: updatedItems, + }; + + return newFormData; + }); + + // Trigger save after state update (but don't close modal) + setTimeout(() => { + saveToSheet(); + }, 100); + }; + const renderField = (field: DynamicFormField, sectionTitle: string) => { const value = formData[sectionTitle]?.[field.label] || ""; @@ -260,6 +432,8 @@ const DynamicInvoiceForm: React.FC = ({ if (!section.itemsConfig || !formData[section.title]) return null; const items = formData[section.title] as any[]; + const maxItems = + section.itemsConfig.range.end - section.itemsConfig.range.start + 1; return ( @@ -271,6 +445,16 @@ const DynamicInvoiceForm: React.FC = ({
Item {index + 1} + {items.length > 1 && ( + handleRemoveItem(section.title, index)} + > + + + )} {Object.entries(section.itemsConfig!.content).map( ([fieldName, cellColumn]) => ( @@ -303,6 +487,19 @@ const DynamicInvoiceForm: React.FC = ({ )}
))} + + {/* Add Item Button */} +
+ handleAddItem(section.title)} + disabled={items.length >= maxItems} + > + + Add Item ({items.length}/{maxItems}) + +
); @@ -366,6 +563,9 @@ const DynamicInvoiceForm: React.FC = ({ + + + @@ -389,24 +589,19 @@ const DynamicInvoiceForm: React.FC = ({ {/* Action Buttons */} -
- +
+ - Save Invoice Data + Save Data - Clear All Data + Clear All
diff --git a/src/components/socialcalc/modules/invoice.js b/src/components/socialcalc/modules/invoice.js index ea09201..b95cb71 100644 --- a/src/components/socialcalc/modules/invoice.js +++ b/src/components/socialcalc/modules/invoice.js @@ -52,6 +52,52 @@ export function getInvoiceCoordinates() { return coordinates; } +// Helper function to clean up HTML entities and unwanted characters +function cleanCellValue(rawValue) { + if (!rawValue) { + return ""; + } + + // Handle numeric values + if (typeof rawValue === "number") { + return rawValue; + } + + // Convert to string and clean up HTML entities + let cleanValue = rawValue.toString(); + let originalValue = cleanValue; // Store original for logging + + // Replace common HTML entities + cleanValue = cleanValue + .replace(/ /g, " ") // Non-breaking space + .replace(/&/g, "&") // Ampersand + .replace(/</g, "<") // Less than + .replace(/>/g, ">") // Greater than + .replace(/"/g, '"') // Double quote + .replace(/'/g, "'") // Single quote + .replace(/'/g, "'") // Apostrophe + .replace(/ /g, " ") // Non-breaking space (numeric) + .replace(/ /g, " ") // Non-breaking space (hex) + .replace(/\u00A0/g, " ") // Unicode non-breaking space + .replace(/\s+/g, " ") // Multiple spaces to single space + .trim(); // Remove leading/trailing whitespace + + // Remove any remaining HTML tags + cleanValue = cleanValue.replace(/<[^>]*>/g, ""); + + // If the cleaned value is just whitespace or empty, return empty string + if (!cleanValue || cleanValue.trim() === "") { + return ""; + } + + // Log if we cleaned something + if (originalValue !== cleanValue) { + console.log(`Cleaned cell value: "${originalValue}" -> "${cleanValue}"`); + } + + return cleanValue; +} + export function addInvoiceData(invoiceData) { return new Promise(function (resolve, reject) { console.log("=== ADD INVOICE DATA START ==="); @@ -218,6 +264,83 @@ export function addInvoiceData(invoiceData) { }); } +export function getDynamicInvoiceData(cellReferences) { + return new Promise(function (resolve, reject) { + console.log("=== GET DYNAMIC INVOICE DATA START ==="); + console.log("Cell references to read:", cellReferences); + + try { + var control = SocialCalc.GetCurrentWorkBookControl(); + console.log("Workbook control:", control ? "Found" : "Not found"); + + if (!control) { + throw new Error("No workbook control available"); + } + + if (!control.currentSheetButton) { + throw new Error("No current sheet button available"); + } + + var currsheet = control.currentSheetButton.id; + console.log("Current active sheet:", currsheet); + + // Get the current sheet object + var sheet = control.workbook.sheetArr[currsheet]?.sheet; + if (!sheet) { + throw new Error("Sheet not found: " + currsheet); + } + + var cellData = {}; + + // Read values from each cell reference + cellReferences.forEach((cellRef) => { + try { + var cell = sheet.cells[cellRef]; + var value = ""; + + if (cell) { + // Get the display value of the cell + if (cell.datatype === "v") { + // Numeric value + value = cell.datavalue !== undefined ? cell.datavalue : ""; + } else if (cell.datatype === "t") { + // Text value + value = cell.datavalue !== undefined ? cell.datavalue : ""; + } else if (cell.datatype === "f") { + // Formula - get the calculated value + value = + cell.valuetype === "n" + ? cell.datavalue + : cell.displaystring || ""; + } else { + // Other types - try to get display value + value = cell.displaystring || cell.datavalue || ""; + } + + // Clean up HTML entities and unwanted characters + value = cleanCellValue(value); + } + + cellData[cellRef] = value; + console.log(`Cell ${cellRef} = ${value}`); + } catch (cellError) { + console.warn(`Error reading cell ${cellRef}:`, cellError); + cellData[cellRef] = ""; + } + }); + + console.log("Retrieved cell data:", cellData); + console.log("=== GET DYNAMIC INVOICE DATA SUCCESS ==="); + resolve(cellData); + } catch (error) { + console.error("=== GET DYNAMIC INVOICE DATA ERROR ==="); + console.error("Error details:", error); + console.error("Stack trace:", error.stack); + reject(error); + } + }); +} + export function addDynamicInvoiceData(cellData, sheetId) { return new Promise(function (resolve, reject) { console.log("=== ADD DYNAMIC INVOICE DATA START ==="); @@ -244,26 +367,32 @@ export function addDynamicInvoiceData(cellData, sheetId) { // Iterate through cellData and create set commands Object.entries(cellData).forEach(([cellRef, value]) => { - if (value !== undefined && value !== null && value !== "") { - // Determine if the value is numeric or text - const stringValue = value.toString().trim(); - const numericValue = parseFloat(stringValue); - - if ( - !isNaN(numericValue) && - isFinite(numericValue) && - stringValue === numericValue.toString() - ) { - // It's a valid number - commands.push(`set ${cellRef} value n ${numericValue}`); + if (value !== undefined && value !== null) { + if (value === "") { + // Clear the cell if value is empty string + commands.push(`set ${cellRef} value`); + console.log(`Clearing cell ${cellRef}`); } else { - // It's text - encode it properly for SocialCalc - const encodedValue = SocialCalc.encodeForSave - ? SocialCalc.encodeForSave(stringValue) - : stringValue; - commands.push(`set ${cellRef} text t ${encodedValue}`); + // Determine if the value is numeric or text + const stringValue = value.toString().trim(); + const numericValue = parseFloat(stringValue); + + if ( + !isNaN(numericValue) && + isFinite(numericValue) && + stringValue === numericValue.toString() + ) { + // It's a valid number + commands.push(`set ${cellRef} value n ${numericValue}`); + } else { + // It's text - encode it properly for SocialCalc + const encodedValue = SocialCalc.encodeForSave + ? SocialCalc.encodeForSave(stringValue) + : stringValue; + commands.push(`set ${cellRef} text t ${encodedValue}`); + } + console.log(`Setting cell ${cellRef} = ${value}`); } - console.log(`Setting cell ${cellRef} = ${value}`); } }); diff --git a/src/utils/dynamicFormManager.ts b/src/utils/dynamicFormManager.ts index ae0a155..62f68b4 100644 --- a/src/utils/dynamicFormManager.ts +++ b/src/utils/dynamicFormManager.ts @@ -26,6 +26,50 @@ export interface ProcessedFormData { * Utility class for managing dynamic form generation based on cell mappings */ export class DynamicFormManager { + /** + * Cleans up HTML entities and unwanted characters from cell values + * @param rawValue The raw value from the cell + * @returns Cleaned string value + */ + private static cleanCellValue(rawValue: any): string { + if (!rawValue) { + return ""; + } + + // Handle numeric values + if (typeof rawValue === "number") { + return rawValue.toString(); + } + + // Convert to string and clean up HTML entities + let cleanValue = rawValue.toString(); + + // Replace common HTML entities + cleanValue = cleanValue + .replace(/ /g, " ") // Non-breaking space + .replace(/&/g, "&") // Ampersand + .replace(/</g, "<") // Less than + .replace(/>/g, ">") // Greater than + .replace(/"/g, '"') // Double quote + .replace(/'/g, "'") // Single quote + .replace(/'/g, "'") // Apostrophe + .replace(/ /g, " ") // Non-breaking space (numeric) + .replace(/ /g, " ") // Non-breaking space (hex) + .replace(/\u00A0/g, " ") // Unicode non-breaking space + .replace(/\s+/g, " ") // Multiple spaces to single space + .trim(); // Remove leading/trailing whitespace + + // Remove any remaining HTML tags + cleanValue = cleanValue.replace(/<[^>]*>/g, ""); + + // If the cleaned value is just whitespace or empty, return empty string + if (!cleanValue || cleanValue.trim() === "") { + return ""; + } + + return cleanValue; + } + /** * Determines the field type based on the field label * @param label The field label @@ -145,7 +189,7 @@ export class DynamicFormManager { } /** - * Initializes form data based on form sections + * Initializes form data based on form sections (starting with minimal items) * @param sections The form sections * @returns Initial form data object */ @@ -154,19 +198,13 @@ export class DynamicFormManager { sections.forEach((section) => { if (section.isItems && section.itemsConfig) { - // Initialize items array + // Initialize items array with just one item const itemsArray: any[] = []; - for ( - let i = section.itemsConfig.range.start; - i <= section.itemsConfig.range.end; - i++ - ) { - const item: any = {}; - Object.keys(section.itemsConfig.content).forEach((contentKey) => { - item[contentKey] = ""; - }); - itemsArray.push(item); - } + const item: any = {}; + Object.keys(section.itemsConfig.content).forEach((contentKey) => { + item[contentKey] = ""; + }); + itemsArray.push(item); formData[section.title] = itemsArray; } else { // Initialize regular fields @@ -260,6 +298,24 @@ export class DynamicFormManager { if (section.isItems && section.itemsConfig) { // Handle items with range-based cell mapping const items = formData[section.title] as any[]; + + // First, clear all cells in the range by setting them to empty string + for ( + let rowIndex = 0; + rowIndex <= + section.itemsConfig.range.end - section.itemsConfig.range.start; + rowIndex++ + ) { + const rowNumber = section.itemsConfig.range.start + rowIndex; + Object.entries(section.itemsConfig.content).forEach( + ([fieldName, columnLetter]) => { + const cellRef = `${columnLetter}${rowNumber}`; + cellData[cellRef] = ""; // Clear the cell + } + ); + } + + // Then, populate cells with actual data if (items && items.length > 0) { items.forEach((item, index) => { const rowNumber = section.itemsConfig!.range.start + index; @@ -285,6 +341,99 @@ export class DynamicFormManager { return cellData; } + /** + * Converts spreadsheet cell data back to form data structure + * @param cellData Object with cell references and their values + * @param sections The form sections to map against + * @returns ProcessedFormData object + */ + static convertFromSpreadsheetFormat( + cellData: { [cellRef: string]: any }, + sections: DynamicFormSection[] + ): ProcessedFormData { + const formData: ProcessedFormData = {}; + + sections.forEach((section) => { + if (section.isItems && section.itemsConfig) { + // Handle items with range-based cell mapping + const itemsArray: any[] = []; + + for ( + let rowIndex = section.itemsConfig.range.start; + rowIndex <= section.itemsConfig.range.end; + rowIndex++ + ) { + const item: any = {}; + let hasData = false; + + Object.entries(section.itemsConfig.content).forEach( + ([fieldName, columnLetter]) => { + const cellRef = `${columnLetter}${rowIndex}`; + const rawValue = cellData[cellRef] || ""; + const cleanValue = this.cleanCellValue(rawValue); + item[fieldName] = cleanValue; + if (cleanValue) hasData = true; + } + ); + + // Only add items that have at least some data + if (hasData || rowIndex === section.itemsConfig.range.start) { + itemsArray.push(item); + } + } + + formData[section.title] = itemsArray; + } else { + // Handle regular fields + const sectionData: any = {}; + section.fields.forEach((field) => { + if (field.cellMapping) { + const rawValue = cellData[field.cellMapping] || ""; + sectionData[field.label] = this.cleanCellValue(rawValue); + } + }); + formData[section.title] = sectionData; + } + }); + + return formData; + } + + /** + * Gets all cell references from form sections + * @param sections The form sections + * @returns Array of cell references + */ + static getAllCellReferences(sections: DynamicFormSection[]): string[] { + const cellRefs: string[] = []; + + sections.forEach((section) => { + if (section.isItems && section.itemsConfig) { + // Add all item cell references + for ( + let rowIndex = section.itemsConfig.range.start; + rowIndex <= section.itemsConfig.range.end; + rowIndex++ + ) { + Object.entries(section.itemsConfig.content).forEach( + ([fieldName, columnLetter]) => { + cellRefs.push(`${columnLetter}${rowIndex}`); + } + ); + } + } else { + // Add regular field cell references + section.fields.forEach((field) => { + if (field.cellMapping) { + cellRefs.push(field.cellMapping); + } + }); + } + }); + + return cellRefs; + } + /** * Gets the active footer from a template * @param template The template data