From 33b506492c36ce876b30c7778b5475b885b944fb Mon Sep 17 00:00:00 2001 From: madhumach <94madhu94@gmail.com> Date: Thu, 5 Mar 2026 20:30:11 -0800 Subject: [PATCH 1/5] add Dataverse upload feature --- samples/Dataverse/src/App.css | 87 +++++ samples/Dataverse/src/App.tsx | 145 +++++--- .../Dataverse/src/components/AccountCard.tsx | 42 +++ .../Dataverse/src/components/AccountForm.tsx | 347 ++++++++++++++++++ .../Dataverse/src/components/AccountList.tsx | 56 +++ samples/Dataverse/src/components/index.ts | 4 + samples/Dataverse/src/hooks/index.ts | 1 + .../Dataverse/src/hooks/useAccountsCrud.ts | 240 ++++++++++++ 8 files changed, 881 insertions(+), 41 deletions(-) create mode 100644 samples/Dataverse/src/components/AccountCard.tsx create mode 100644 samples/Dataverse/src/components/AccountForm.tsx create mode 100644 samples/Dataverse/src/components/AccountList.tsx create mode 100644 samples/Dataverse/src/hooks/useAccountsCrud.ts diff --git a/samples/Dataverse/src/App.css b/samples/Dataverse/src/App.css index dda1952..420ccd2 100644 --- a/samples/Dataverse/src/App.css +++ b/samples/Dataverse/src/App.css @@ -64,6 +64,41 @@ header p { position: relative; } +/* Page Navigation Tabs */ +.page-tabs { + display: flex; + gap: 8px; + margin-bottom: 24px; + border-bottom: 2px solid #c5b4e3; + padding-bottom: 0; +} + +.tab-btn { + padding: 10px 28px; + border: none; + background: transparent; + font-size: 1rem; + font-family: 'Segoe UI', 'Segoe Sans Text', sans-serif; + font-weight: 500; + color: #702573; + cursor: pointer; + border-bottom: 3px solid transparent; + margin-bottom: -2px; + border-radius: 8px 8px 0 0; + transition: background 0.15s, color 0.15s; +} + +.tab-btn:hover { + background: rgba(197, 180, 227, 0.2); +} + +.tab-btn.active { + color: #c03bc4; + border-bottom-color: #c03bc4; + background: rgba(192, 59, 196, 0.08); + font-weight: 600; +} + /* Error message */ .error-message { background: linear-gradient(135deg, #fee 0%, #fdd 100%); @@ -465,6 +500,58 @@ footer code { font-weight: 500; } +/* Attachment sub-form */ +.attachment-subform { + margin-top: 32px; + padding: 20px 24px; + background: rgba(197, 180, 227, 0.12); + border: 1px solid rgba(197, 180, 227, 0.4); + border-radius: 12px; +} + +.attachment-subform h3 { + margin: 0 0 16px 0; + font-size: 1rem; + font-weight: 600; + color: #702573; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.attachment-actions { + margin-top: 8px; +} + +.required-mark { + color: #c03bc4; + margin-left: 2px; +} + +/* Upload toast */ +.upload-toast { + position: sticky; + top: 0; + z-index: 100; + padding: 14px 20px; + border-radius: 10px; + margin-bottom: 16px; + font-weight: 500; + font-size: 0.95rem; + animation: fadeIn 0.25s ease-out; +} + +.upload-toast--success { + background: linear-gradient(135deg, #e6f9f0 0%, #d4f5e5 100%); + border: 1px solid #a3d9b8; + color: #1a6640; +} + +.upload-toast--error { + background: linear-gradient(135deg, #fee 0%, #fdd 100%); + border: 1px solid #fcc; + color: #c33; +} + /* Smooth animations */ @keyframes fadeIn { from { diff --git a/samples/Dataverse/src/App.tsx b/samples/Dataverse/src/App.tsx index 28babd5..a02103e 100644 --- a/samples/Dataverse/src/App.tsx +++ b/samples/Dataverse/src/App.tsx @@ -2,8 +2,9 @@ * Dataverse Demo App - Main Application Component * * This app demonstrates how to use Power Apps code apps with Dataverse: - * - CRUD operations (Create, Read, Update, Delete) on Contact entities + * - CRUD operations (Create, Read, Update, Delete) on Contact and Account entities * - Lookup fields (linking Contacts to Accounts) + * - File upload (AccountForm attachment sub-form) * - Error handling and best practices * - Component-based architecture * @@ -14,12 +15,13 @@ * 1. COMPONENTS (Presentation Layer): * - Pure presentational components (UI only, no business logic) * - Receive data via props, emit events via callbacks - * - Examples: Header, ContactList, ContactForm, ErrorMessage + * - Examples: Header, ContactList, ContactForm, AccountList, AccountForm, + * ErrorMessage * * 2. HOOKS (Business Logic Layer): * - Custom hooks that manage state and orchestrate services * - Handle error handling, loading states, and data transformations - * - Examples: useContacts, useAccounts, useLookupResolver + * - Examples: useContacts, useAccounts, useAccountsCrud, useLookupResolver * * 3. SERVICES (Data Access Layer): * - Auto-generated by PAC CLI from Dataverse metadata @@ -38,35 +40,56 @@ * - Clarity: Clear boundaries between presentation, logic, and data access */ +import { useState } from 'react'; import { Header, ErrorMessage, ContactList, ContactForm, + AccountList, + AccountForm, Footer, -} from "./components"; -import { useContacts, useAccounts } from "./hooks"; -import "./App.css"; +} from './components'; +import { useContacts, useAccounts, useAccountsCrud } from './hooks'; +import './App.css'; + +type ActivePage = 'contacts' | 'accounts'; function App() { + const [activePage, setActivePage] = useState('contacts'); + // PATTERN: Custom hooks handle all the business logic and state management // The component stays simple and focused on rendering UI const { - contacts, // Array of contact records - loading, // Loading state for initial data fetch - error, // Error message string (null if no error) - selectedContact, // Currently selected contact for editing (null if none) - isCreating, // Flag indicating if we're in "create new" mode - startCreate, // Function to enter create mode - selectContact, // Function to select a contact for editing - cancelForm, // Function to cancel create/edit and return to list - handleFormSubmit, // Function to handle form submission (routes to create or update) - deleteContact, // Function to delete a contact + contacts, + loading: contactsLoading, + error: contactsError, + selectedContact, + isCreating: isCreatingContact, + startCreate: startCreateContact, + selectContact, + cancelForm: cancelContactForm, + handleFormSubmit: handleContactFormSubmit, + deleteContact, } = useContacts(); - // Load accounts for the Managing Partner lookup dropdown + // Load accounts for the Managing Partner lookup dropdown in the Contact form const { accounts } = useAccounts(); + // Full CRUD hook for the Accounts page + const { + accounts: accountsList, + loading: accountsLoading, + error: accountsError, + selectedAccount, + isCreating: isCreatingAccount, + startCreate: startCreateAccount, + selectAccount, + cancelForm: cancelAccountForm, + handleFormSubmit: handleAccountFormSubmit, + deleteAccount, + } = useAccountsCrud(); + return (
{/* Header Section */} @@ -75,32 +98,72 @@ function App() { description="Demonstrating CRUD operations with Power Apps Code Apps" /> - {/* Error Display - Shows only when error exists */} - + {/* Page Navigation Tabs */} + - {/* Main Content Grid - Two column layout (list + form) */} -
- {/* Left Column: Contacts List */} - + {/* Contacts Page */} + {activePage === 'contacts' && ( + <> + +
+ + {(isCreatingContact || selectedContact) && ( + + )} +
+ + )} - {/* Right Column: Contact Form (shown only when creating or editing) */} - {(isCreating || selectedContact) && ( - - )} -
+ {/* Accounts Page */} + {activePage === 'accounts' && ( + <> + +
+ + {(isCreatingAccount || selectedAccount) && ( + + )} +
+ + )} {/* Footer Section */}