diff --git a/astro.config.mjs b/astro.config.mjs index 2e4770c70..471246bde 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -758,32 +758,25 @@ async function config() { items: [ { label: 'Overview', link: '/dropins-b2b/' }, { - label: 'B2B Containers', - collapsed: true, - items: [ - { label: 'RequestNegotiableQuoteForm', link: '/dropins-b2b/container/request-negotiable-quote-form/' }, - ], - }, - { - label: 'Checkout', + label: 'Company Management', collapsed: true, items: [ - { - label: 'Containers', - collapsed: false, - items: [ - { label: 'PaymentOnAccount', link: '/dropins-b2b/checkout/containers/payment-on-account/' }, - { label: 'PurchaseOrder', link: '/dropins-b2b/checkout/containers/purchase-order/' }, - ], - }, + { label: 'Functions', link: '/dropins-b2b/company-management/functions/' }, + { label: 'Events', link: '/dropins-b2b/company-management/events/' }, ], }, { - label: 'Company Management', + label: 'Quote Management', collapsed: true, items: [ - { label: 'Functions', link: '/dropins-b2b/company-management/functions/' }, - { label: 'Events', link: '/dropins-b2b/company-management/events/' }, + { label: 'Overview', link: '/dropins-b2b/quote-management/' }, + { label: 'Installation', link: '/dropins-b2b/quote-management/installation/' }, + { label: 'Initialization', link: '/dropins-b2b/quote-management/initialization/' }, + { label: 'Containers', link: '/dropins-b2b/quote-management/containers/' }, + { label: 'Functions', link: '/dropins-b2b/quote-management/functions/' }, + { label: 'Events', link: '/dropins-b2b/quote-management/events/' }, + { label: 'Styles', link: '/dropins-b2b/quote-management/styles/' }, + { label: 'Dictionary', link: '/dropins-b2b/quote-management/dictionary/' }, ], }, ], diff --git a/src/content/docs/dropins-b2b/index.mdx b/src/content/docs/dropins-b2b/index.mdx index 461a0de2b..6b8c8d811 100644 --- a/src/content/docs/dropins-b2b/index.mdx +++ b/src/content/docs/dropins-b2b/index.mdx @@ -1,6 +1,7 @@ --- title: B2B Commerce Overview description: Learn about Adobe Commerce B2B solutions for business-to-business commerce implementations. +topic: Storefront Developers --- import { Card, CardGrid } from '@astrojs/starlight/components'; diff --git a/src/content/docs/dropins-b2b/quote-management/containers.mdx b/src/content/docs/dropins-b2b/quote-management/containers.mdx new file mode 100644 index 000000000..75dd2377c --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/containers.mdx @@ -0,0 +1,254 @@ +--- +title: Quote Management containers +description: Learn about the container components available in the quote management drop-in. +--- + +import { Card, CardGrid } from '@astrojs/starlight/components'; +import OptionsTable from '@components/OptionsTable.astro'; + +The quote management drop-in provides three main container components that handle different aspects of the B2B quote workflow. Each container is designed to be flexible and customizable to meet your specific B2B requirements. + +## Available Containers + + + + Enables customers to request new negotiable quotes from their cart contents with form validation and draft saving capabilities. + + + Provides comprehensive quote management including status updates, product management, and quote actions. + + + Displays a summary of quoted items with pricing details and management actions. + + + Displays a comprehensive list of quotes with pagination, sorting, and bulk actions. + + + +## RequestNegotiableQuoteForm + +The `RequestNegotiableQuoteForm` container enables customers to request new negotiable quotes from their current cart contents. + +### Features + +- Quote name and comment input fields +- Form validation with error handling +- Draft saving functionality +- Success/error messaging +- Integration with cart contents +- Authentication checks + +### Configuration options + + + +### Example usage + +```js +provider.render(RequestNegotiableQuoteForm, { + cartId: 'cart-123', + onAttachFiles: async (files) => { + console.log('Files attached:', files); + // Handle file upload + }, + onRequestNegotiableQuote: async (data) => { + console.log('Quote requested:', data); + // Custom quote request logic + return null; + }, + onSaveNegotiableQuote: async (data) => { + console.log('Draft saved:', data); + // Custom draft save logic + return null; + }, + onSubmitErrors: (errors) => { + console.log('Form validation errors:', errors); + // Handle validation errors + }, + onError: (props) => { + console.error('Error occurred:', props.error); + // Handle error state + }, +})(document.getElementById('request-quote-form')); +``` + +## ManageNegotiableQuote + +The `ManageNegotiableQuote` container provides comprehensive quote management capabilities for existing quotes. + +### Features + +- Quote details display (creation date, sales rep, expiration) +- Quote status management and updates +- Product list with pricing and quantity controls +- Quote actions (print, copy, delete, send for review) +- Shipping information display +- Quote comments section +- Permission-based action availability + +### Configuration options + + + +### Example usage + +```js +provider.render(ManageNegotiableQuote, { + onActionsDropdownChange: (event) => { + console.log('Actions dropdown changed:', event); + // Handle dropdown change + }, + onActionsButtonClick: (action) => { + console.log('Action button clicked:', action); + // Handle specific actions (close, delete, print, createTemplate, createCopy) + }, + onSendForReview: (quoteData) => { + console.log('Send for review:', quoteData); + // Handle send for review action + }, +})(document.getElementById('manage-quote')); +``` + +## ItemsQuoted + +The `ItemsQuoted` container displays a summary of items that have been quoted, providing a quick overview of quoted products. + +### Features + +- Product information and pricing display +- Quantity and discount details +- Subtotal calculations +- Action buttons for quote management +- Responsive design for mobile and desktop + +### Configuration options + + + +### Example usage + +```js +provider.render(ItemsQuoted, { + quoteData: quoteData, // Optional: provide initial data + onItemCheckboxChange: (item, isSelected) => { + console.log('Item checkbox changed:', item, isSelected); + // Handle checkbox change + }, + onItemDropdownChange: (item, action) => { + console.log('Item dropdown changed:', item, action); + // Handle dropdown change + }, + onUpdate: (e) => { + console.log('Update button clicked:', e); + // Handle form submission + }, +})(document.getElementById('items-quoted')); +``` + +## QuotesListTable + +The `QuotesListTable` container displays a comprehensive list of quotes with advanced filtering, sorting, and management capabilities. + +### Features + +- Quote list display with status indicators +- Pagination and sorting controls +- Search and filtering options +- Bulk actions for multiple quotes +- Quote selection and navigation +- Responsive table design + +### Configuration options + + + +### Example usage + +```js +provider.render(QuotesListTable, { + pageSize: 25, + showItemRange: true, + showPageSizePicker: true, + showPagination: true, + onViewQuote: (quoteId, quoteName, status) => { + console.log('View quote:', quoteId, quoteName, status); + // Navigate to quote management page + window.location.href = `/quotes/${quoteId}`; + }, + onPageSizeChange: (newPageSize) => { + console.log('Page size changed:', newPageSize); + // Handle page size change + }, + onPageChange: (newPage) => { + console.log('Page changed:', newPage); + // Handle page change + }, +})(document.getElementById('quotes-list-table')); +``` + +## Container Integration + +These containers are designed to work together to provide a complete quote management experience: + +1. **RequestNegotiableQuoteForm** - Used on cart pages or product pages to initiate quote requests +2. **ManageNegotiableQuote** - Used on dedicated quote management pages for comprehensive quote handling +3. **ItemsQuoted** - Used on quote summary pages or as a sidebar component +4. **QuotesListTable** - Used on quotes listing pages for browsing and managing multiple quotes + +## Customization + +Each container can be customized through: + +- **Props**: Configuration options passed to the container +- **Slots**: Content areas that can be customized with your own components +- **Events**: Callback functions for handling user interactions +- **Styles**: CSS customization for visual appearance + +## Best Practices + +- Use `RequestNegotiableQuoteForm` on cart pages to enable easy quote requests +- Implement `ManageNegotiableQuote` on dedicated quote management pages +- Use `ItemsQuoted` as a summary component in quote lists or sidebars +- Handle errors gracefully with appropriate callback functions +- Ensure proper authentication checks before rendering containers +- Test all quote workflows thoroughly in your B2B environment \ No newline at end of file diff --git a/src/content/docs/dropins-b2b/quote-management/dictionary.mdx b/src/content/docs/dropins-b2b/quote-management/dictionary.mdx new file mode 100644 index 000000000..bde32a4d3 --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/dictionary.mdx @@ -0,0 +1,268 @@ +--- +title: Quote Management dictionary +description: Learn about the internationalization (i18n) dictionary for the quote management drop-in component. +--- + +The quote management drop-in component includes comprehensive internationalization support through dictionary files. These files contain all the text strings used throughout the component, making it easy to localize for different languages and regions. + +## Dictionary Structure + +The quote management dictionary follows a hierarchical structure organized by component and feature: + +```json +{ + "NegotiableQuote": { + "Request": { + "title": "Request a Quote", + "comment": "Comment", + "commentError": "Please add your comment", + "quoteName": "Quote name", + "quoteNameError": "Please add a quote name", + "attachmentsError": "Error uploading attachments", + "requestCta": "Request a Quote", + "saveDraftCta": "Save as draft", + "error": { + "header": "Error", + "unauthenticated": "Please sign in to request a quote.", + "unauthorized": "You are not authorized to request a quote.", + "missingCart": "Could not find a valid cart." + }, + "success": { + "header": "Success", + "submitted": "Quote request submitted successfully!", + "draftSaved": "Quote saved as draft successfully!" + } + }, + "Manage": { + "createdLabel": "Created:", + "salesRepLabel": "Sales Rep:", + "expiresLabel": "Expires:", + "actionsLabel": "Actions", + "actions": { + "remove": "Remove" + }, + "bannerTitle": "Alert", + "bannerStatusMessages": { + "pending": "This quote is currently locked for editing. It will become available once released by the Merchant.", + "expired": "Your quote has expired and the product prices have been updated as per the latest prices in your catalog. You can either re-submit the quote to seller for further negotiation or go to checkout." + }, + "actionButtons": { + "close": "Close quote", + "delete": "Delete quote", + "print": "Print quote", + "createTemplate": "Create quote template", + "createCopy": "Create copy", + "sendForReview": "Send for review" + }, + "shippingInformation": { + "title": "Shipping Information" + }, + "quoteComments": { + "title": "Quote Comments", + "placeholder": "Add your comment" + }, + "productListTable": { + "headers": { + "productName": "Product name", + "sku": "SKU", + "price": "Price", + "quantity": "Quantity", + "discount": "Discount", + "subtotal": "Subtotal", + "actions": "Actions" + }, + "submitButton": "Update", + "actions": { + "editNoteToSeller": "Edit note to seller", + "remove": "Remove" + } + }, + "quotePricesSummary": { + "subtotal": { + "excludingTax": "Quote Subtotal (excluding tax)" + }, + "appliedTaxes": "Applied Taxes", + "grandTotal": { + "includingTax": "Quote Grand Total (including tax)" + } + } + } + } +} +``` + +## Dictionary Categories + +### Request Category + +Contains all text strings related to the quote request form: + +- **Form Labels**: Field labels for quote name, comments, and attachments +- **Error Messages**: Validation and error messages for form submission +- **Success Messages**: Confirmation messages for successful operations +- **Action Buttons**: Text for request and draft save buttons + +### Manage Category + +Contains all text strings related to quote management: + +- **Quote Details**: Labels for creation date, sales rep, expiration +- **Status Messages**: Banner messages for different quote states +- **Action Buttons**: Text for quote management actions +- **Product Table**: Headers and labels for product list display +- **Pricing Summary**: Labels for pricing calculations + +## Customizing Dictionary Entries + +You can customize dictionary entries by overriding them in your initialization configuration: + +```js +// Custom dictionary overrides +const customDictionary = { + NegotiableQuote: { + Request: { + title: "Request Custom Quote", + requestCta: "Submit Quote Request", + success: { + submitted: "Your custom quote request has been submitted!" + } + }, + Manage: { + actionButtons: { + print: "Generate PDF", + createTemplate: "Save as Template" + } + } + } +}; + +// Merge with default dictionary +const finalDictionary = { + ...defaultDictionary, + ...customDictionary +}; +``` + +## Adding New Languages + +To add support for a new language, create a new dictionary file following the same structure: + +```json +// fr_FR.json +{ + "NegotiableQuote": { + "Request": { + "title": "Demander un devis", + "comment": "Commentaire", + "quoteName": "Nom du devis", + "requestCta": "Demander un devis", + "saveDraftCta": "Enregistrer comme brouillon" + }, + "Manage": { + "createdLabel": "Créé :", + "salesRepLabel": "Commercial :", + "expiresLabel": "Expire :", + "actionButtons": { + "close": "Fermer le devis", + "delete": "Supprimer le devis", + "print": "Imprimer le devis" + } + } + } +} +``` + +## Loading Dictionary Files + +Load dictionary files during initialization: + +```js +// Load multiple language dictionaries +const en_US = await fetch('/i18n/en_US.json').then((res) => res.json()); +const fr_FR = await fetch('/i18n/fr_FR.json').then((res) => res.json()); +const de_DE = await fetch('/i18n/de_DE.json').then((res) => res.json()); + +// Configure language definitions +const langDefinitions = { + default: en_US, + en_US, + fr_FR, + de_DE, +}; + +// Initialize with language support +initializers.register(api.initialize, { + langDefinitions, +}); +``` + +## Dynamic Language Switching + +You can switch languages dynamically at runtime: + +```js +// Switch to French +await api.setLanguage('fr_FR'); + +// Switch to German +await api.setLanguage('de_DE'); + +// Get current language +const currentLanguage = api.getCurrentLanguage(); +console.log('Current language:', currentLanguage); +``` + +## Best Practices + +- **Consistency**: Use consistent terminology across all dictionary entries +- **Context**: Provide context for translators through clear key names +- **Completeness**: Ensure all user-facing text is included in the dictionary +- **Testing**: Test all languages thoroughly to ensure proper display +- **Fallbacks**: Always provide fallback text for missing translations +- **Pluralization**: Consider plural forms for different languages +- **Cultural Adaptation**: Adapt content for cultural differences, not just language + +## Common Dictionary Patterns + +### Error Messages +```json +"error": { + "header": "Error", + "unauthenticated": "Please sign in to request a quote.", + "unauthorized": "You are not authorized to request a quote.", + "validation": { + "required": "This field is required", + "invalid": "Please enter a valid value" + } +} +``` + +### Action Buttons +```json +"actionButtons": { + "primary": "Submit", + "secondary": "Cancel", + "tertiary": "Save Draft", + "destructive": "Delete" +} +``` + +### Status Messages +```json +"statusMessages": { + "loading": "Loading...", + "success": "Operation completed successfully", + "error": "An error occurred", + "warning": "Please review your input" +} +``` + +### Form Labels +```json +"formLabels": { + "required": "Required", + "optional": "Optional", + "placeholder": "Enter your text here", + "helpText": "This information will be used for..." +} +``` \ No newline at end of file diff --git a/src/content/docs/dropins-b2b/quote-management/events.mdx b/src/content/docs/dropins-b2b/quote-management/events.mdx new file mode 100644 index 000000000..e15529363 --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/events.mdx @@ -0,0 +1,287 @@ +--- +title: Quote Management events +description: Learn about the events emitted by the quote management drop-in component. +--- + +import { Card, CardGrid } from '@astrojs/starlight/components'; +import OptionsTable from '@components/OptionsTable.astro'; + +The quote management drop-in uses an event-driven architecture to communicate with other components and enable real-time updates. This allows for seamless integration with other storefront components and provides flexibility for custom implementations. + +## Event System + +The quote management drop-in emits events through the Adobe Commerce Event Bus (`@adobe-commerce/event-bus`). You can listen to these events to: + +- Update UI components in real-time +- Integrate with other storefront components +- Implement custom business logic +- Track user interactions and analytics + +## Core Events + +### quote-management/negotiable-quote-requested + +Emitted when a new negotiable quote is successfully requested. + + + +**Input object properties:** +- `cartId` (string): Cart ID used for the request +- `quoteName` (string): Name for the quote +- `comment` (string, optional): Comment for the quote +- `isDraft` (boolean, optional): Whether the quote is a draft + +**Example:** +```js +import { events } from '@adobe-commerce/event-bus'; + +events.on('quote-management/negotiable-quote-requested', (data) => { + console.log('New quote created:', data.quote); + console.log('Input parameters:', data.input); + + // Update UI to show success message + showNotification('Quote requested successfully!'); + + // Navigate to quote management page + if (data.quote) { + navigateToQuoteManagement(data.quote.id); + } +}); +``` + +### quote-management/quote-data + +Emitted when quote data is retrieved or updated. + + + +**Example:** +```js +events.on('quote-management/quote-data', (data) => { + console.log('Quote data loaded:', data.quote); + console.log('User permissions:', data.permissions); + + // Update quote display components + updateQuoteDisplay(data.quote); + + // Enable/disable actions based on permissions + updateActionButtons(data.permissions); +}); +``` + +### quote-management/quote-data/initialized + +Emitted when quote data is initially loaded during drop-in initialization. + + + +**Example:** +```js +events.on('quote-management/quote-data/initialized', (data) => { + console.log('Quote initialized:', data.quote); + console.log('User permissions:', data.permissions); + + // Initialize UI with quote data + initializeQuoteDisplay(data.quote); +}); +``` + +### quote-management/quote-data/error + +Emitted when an error occurs while loading quote data. + + + +**Example:** +```js +events.on('quote-management/quote-data/error', (data) => { + console.error('Error loading quote data:', data.error); + + // Show error message to user + showErrorMessage('Failed to load quote data'); +}); +``` + +### quote-management/permissions + +Emitted when user permissions are updated or refreshed. + + + +**Example:** +```js +events.on('quote-management/permissions', (data) => { + console.log('Permissions updated:', data.permissions); + + // Update UI based on new permissions + updateActionButtons(data.permissions); + + // Show/hide features based on permissions + toggleFeatures(data.permissions); +}); +``` + +## External Events + +The quote management drop-in also listens to external events from other parts of the application: + +### authenticated + +Emitted when user authentication status changes. + +**Example:** +```js +events.on('authenticated', (isAuthenticated) => { + console.log('Authentication status:', isAuthenticated); + + // Update UI based on authentication status + if (isAuthenticated) { + showAuthenticatedFeatures(); + } else { + hideAuthenticatedFeatures(); + } +}); +``` + +### locale + +Emitted when the application locale changes. + +**Example:** +```js +events.on('locale', (locale) => { + console.log('Locale changed to:', locale); + + // Update UI language + updateUILanguage(locale); +}); +``` + +## Integration Examples + +### Cart Integration + +Listen for quote events to update cart components: + +```js +// When a quote is requested from cart +events.on('quote-management/negotiable-quote-requested', (data) => { + if (data.quote) { + // Clear cart after successful quote request + clearCart(); + + // Show success message + showCartNotification('Items moved to quote successfully'); + } +}); + +// When quote data is loaded +events.on('quote-management/quote-data', (data) => { + // Update UI with quote information + updateQuoteDisplay(data.quote); +}); +``` + +### Navigation Integration + +Handle navigation based on quote events: + +```js +// Navigate to quote management after creation +events.on('quote-management/negotiable-quote-requested', (data) => { + if (data.quote) { + window.location.href = `/quotes/${data.quote.id}`; + } +}); + +// Navigate to quotes list after error +events.on('quote-management/quote-data/error', (data) => { + console.error('Failed to load quote:', data.error); + window.location.href = '/quotes'; +}); +``` + +### Analytics Integration + +Track user interactions for analytics: + +```js +// Track quote requests +events.on('quote-management/negotiable-quote-requested', (data) => { + if (data.quote) { + analytics.track('Quote Requested', { + quoteId: data.quote.id, + quoteName: data.input.quoteName, + cartId: data.input.cartId, + isDraft: data.input.isDraft || false + }); + } +}); + +// Track quote data loading +events.on('quote-management/quote-data', (data) => { + analytics.track('Quote Data Loaded', { + quoteId: data.quote.id, + permissions: Object.keys(data.permissions) + }); +}); +``` + +## Event Cleanup + +Remember to clean up event listeners when components are unmounted: + +```js +// Store reference to event handler +const handleQuoteRequested = (data) => { + console.log('Quote requested:', data); +}; + +// Add event listener and store the subscription +const subscription = events.on('quote-management/negotiable-quote-requested', handleQuoteRequested); + +// Clean up when component unmounts +const cleanup = () => { + subscription.off(); +}; + +// Call cleanup when appropriate +window.addEventListener('beforeunload', cleanup); +``` + +## Best Practices + +- Always handle errors appropriately in event listeners +- Use descriptive event handler names for better debugging +- Clean up event listeners to prevent memory leaks +- Use events for loose coupling between components +- Implement proper error boundaries for event handling +- Consider using TypeScript for better type safety with events \ No newline at end of file diff --git a/src/content/docs/dropins-b2b/quote-management/functions.mdx b/src/content/docs/dropins-b2b/quote-management/functions.mdx new file mode 100644 index 000000000..ec6eec77d --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/functions.mdx @@ -0,0 +1,248 @@ +--- +title: Quote Management functions +description: Learn about the API functions available in the quote management drop-in component. +--- + +import OptionsTable from '@components/OptionsTable.astro'; + +The quote management drop-in provides a comprehensive set of API functions for managing negotiable quotes in B2B commerce scenarios. These functions handle everything from requesting new quotes to managing existing ones. + +## Core Functions + +### requestNegotiableQuote + +Creates a new negotiable quote request from cart contents. + + + +**Returns:** `Promise` + +**Example:** +```js +import { requestNegotiableQuote } from '@dropins/storefront-quote-management/api.js'; + +try { + const quote = await requestNegotiableQuote({ + cartId: 'cart-123', + quoteName: 'Q1 2024 Office Supplies', + comment: 'Please provide volume discount pricing', + isDraft: false + }); + + console.log('Quote created:', quote); +} catch (error) { + console.error('Failed to create quote:', error); +} +``` + +### getQuoteData + +Retrieves detailed information about a specific negotiable quote. + + + +**Returns:** `Promise` + +**Example:** +```js +import { getQuoteData } from '@dropins/storefront-quote-management/api.js'; + +try { + const quote = await getQuoteData('quote-123'); + console.log('Quote data:', quote); +} catch (error) { + console.error('Failed to get quote:', error); +} +``` + +### getCustomerData + +Retrieves customer information and permissions for B2B operations. This function does not require any parameters. Instead it uses the current authenticated session to fetch the data. + +**Returns:** `Promise` + +**Example:** +```js +import { getCustomerData } from '@dropins/storefront-quote-management/api.js'; + +try { + const customer = await getCustomerData(); + console.log('Customer data:', customer); + console.log('Permissions:', customer.permissions); +} catch (error) { + console.error('Failed to get customer data:', error); +} +``` + +## Data Models + +The quote management drop-in uses several TypeScript interfaces to define data structures: + +### NegotiableQuoteModel + +Represents a negotiable quote with all its properties and relationships. + +```typescript +interface NegotiableQuoteModel { + uid: string; + name: string; + createdAt: string; + salesRepName: string; + expirationDate: string; + updatedAt: string; + status: NegotiableQuoteStatus; + buyer: { + firstname: string; + lastname: string; + }; + templateName?: string; + comments?: QuoteComment[]; + prices: QuotePrices; + items: NegotiableQuoteCartItem[]; + canCheckout: boolean; + canSendForReview: boolean; +} +``` + +### NegotiableQuoteStatus + +Enumeration of possible quote statuses. + +```typescript +enum NegotiableQuoteStatus { + SUBMITTED = 'SUBMITTED', + PENDING = 'PENDING', + UPDATED = 'UPDATED', + OPEN = 'OPEN', + ORDERED = 'ORDERED', + CLOSED = 'CLOSED', + DECLINED = 'DECLINED', + EXPIRED = 'EXPIRED', + DRAFT = 'DRAFT', +} +``` + +### CustomerModel + +Represents customer data and permissions. + +```typescript +interface CustomerModel { + permissions: CustomerPermissions; +} + +interface CustomerPermissions { + canRequestQuote: boolean; + canEditQuote: boolean; + canDeleteQuote: boolean; + canCheckoutQuote: boolean; +} +``` + +## Utility Functions + +### negotiableQuotes + +Retrieves a list of negotiable quotes for the current customer with filtering, sorting, and pagination. + + + +**Returns:** `Promise` + +**Example:** +```js +import { negotiableQuotes } from '@dropins/storefront-quote-management/api.js'; + +try { + const result = await negotiableQuotes({ + filter: { + name: { + match: 'Office Supplies', + match_type: 'PARTIAL' + } + }, + pageSize: 25, + currentPage: 1, + sort: { + sort_field: 'CREATED_AT', + sort_direction: 'DESC' + } + }); + console.log('Quotes:', result.items); + console.log('Pagination:', result.pageInfo); +} catch (error) { + console.error('Failed to get quotes list:', error); +} +``` + +## Error Handling + +All functions return promises and should be wrapped in try-catch blocks for proper error handling: + +```js +try { + const result = await someQuoteFunction(parameters); + // Handle success +} catch (error) { + // Handle error + console.error('Operation failed:', error.message); + + // Check for specific error types + if (error.message.includes('Unauthorized')) { + // Handle authentication error + } else if (error.message.includes('Not Found')) { + // Handle not found error + } +} +``` + +## Event Integration + +Many functions emit events that you can listen to for real-time updates: + +```js +import { events } from '@adobe-commerce/event-bus'; + +// Listen for quote events +events.on('quote-management/negotiable-quote-requested', (data) => { + console.log('Quote requested:', data.quote); +}); + +events.on('quote-management/quote-data', (data) => { + console.log('Quote data updated:', data.quote); +}); + +events.on('quote-management/quote-data/initialized', (data) => { + console.log('Quote initialized:', data.quote); +}); +``` + +## Best Practices + +- Always handle errors appropriately with try-catch blocks +- Check user permissions before performing operations +- Use events for real-time UI updates +- Validate input parameters before making API calls +- Implement proper loading states for better UX +- Cache frequently accessed data when appropriate \ No newline at end of file diff --git a/src/content/docs/dropins-b2b/quote-management/index.mdx b/src/content/docs/dropins-b2b/quote-management/index.mdx new file mode 100644 index 000000000..4e53a1dc5 --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/index.mdx @@ -0,0 +1,140 @@ +--- +title: Quote Management overview +description: Learn about the features and functions of the quote management drop-in component for B2B commerce. +sidebar: + order: 1 +--- + +import { Badge } from '@astrojs/starlight/components'; +import OptionsTable from '@components/OptionsTable.astro'; + +The quote management drop-in component provides comprehensive B2B quote functionality, enabling customers to request, manage, and track negotiable quotes directly within the storefront experience. This component is designed specifically for B2B commerce scenarios where complex pricing negotiations and approval workflows are essential. + +## Supported Commerce features + +The following table provides an overview of the Adobe Commerce B2B features that the quote management drop-in supports: + +| Feature | Status | +| ---------------------------------------------------------------- | ------------------------------------------ | +| Request negotiable quotes | | +| Quote management and tracking | | +| Quote status updates | | +| Quote comments and attachments | | +| Quote pricing summary | | +| Product list management | | +| Quote actions (print, copy, delete) | | +| Draft quote saving | | +| Quote expiration handling | | +| Customer authentication integration | | +| Permission-based access control | | +| Event-driven architecture | | +| Internationalization (i18n) support | | +| Responsive design | | + +## Key Components + +The quote management drop-in consists of several container and UI components: + +### Container Components + +#### RequestNegotiableQuoteForm +Enables customers to request new negotiable quotes from their cart contents. This component handles: +- Quote name and comment input +- Draft saving functionality +- Form validation and error handling +- Success/error messaging + +#### ManageNegotiableQuote +Provides comprehensive quote management capabilities including: +- Quote details display (creation date, sales rep, expiration) +- Quote status management and updates +- Product list with pricing and quantity controls +- Quote actions (print, copy, delete, send for review) +- Shipping information display +- Quote comments section + +#### ItemsQuoted +Displays a summary of items that have been quoted, providing: +- Product information and pricing +- Quantity and discount details +- Subtotal calculations +- Action buttons for quote management + +#### QuotesListTable +Displays a comprehensive list of quotes with: +- Quote status and metadata +- Pagination and sorting capabilities +- Bulk actions for quote management +- Search and filtering options + +### UI Components + +#### TabbedContent +Provides tabbed interface for organizing quote management sections: +- Quote details tab +- Product list tab +- Comments tab +- Actions tab + +#### ActionsBar +Displays action buttons for quote operations: +- Print quote +- Copy quote +- Delete quote +- Send for review +- Checkout quote + +#### ProductListTable +Shows detailed product information within quotes: +- Product details and pricing +- Quantity controls +- Discount information +- Configurable and bundle options + +#### QuotePricesSummary +Displays pricing breakdown for quotes: +- Subtotal calculations +- Tax information +- Applied discounts +- Grand total + +## Section topics + +The topics in this section will help you understand how to customize and use the quote management drop-in effectively within your B2B storefront. + +### Installation + +Provides the step-by-step process for embedding the quote management drop-in into your B2B site. This topic covers everything from basic setup requirements to advanced configurations, ensuring seamless integration with your existing B2B architecture. Visit the [quote management installation](/dropins-b2b/quote-management/installation/) page to get started. + +### Initialization + +Describes how to configure the quote management drop-in initializer with language definitions, permissions, and custom models. This customization allows you to align the drop-in with your B2B workflow requirements and brand standards. Visit the [quote management initialization](/dropins-b2b/quote-management/initialization/) page to learn more. + +### Containers + +Describes the structural elements of the quote management drop-in, focusing on how each container manages and displays content. Includes configuration options and customization settings to optimize the B2B user experience. Visit the [quote management containers](/dropins-b2b/quote-management/containers/) page to learn more. + +### Functions + +Describes the API functions available in the quote management drop-in. These functions allow developers to retrieve quote data, request new quotes, and manage quote lifecycle operations programmatically. Visit the [quote management functions](/dropins-b2b/quote-management/functions/) page to learn more. + +### Events + +Explains the event-driven architecture of the quote management drop-in, including available events and how to listen for them to integrate with other storefront components. Visit the [quote management events](/dropins-b2b/quote-management/events/) page to learn more. + +### Styles + +Describes how to customize the appearance of the quote management drop-in using CSS. Provides guidelines and examples for applying styles to various components within the drop-in to maintain brand consistency. Visit the [quote management styles](/dropins-b2b/quote-management/styles/) page to learn more. + +## B2B Integration + +The quote management drop-in is designed to work seamlessly with other B2B storefront components: + +- **User Authentication**: Integrates with user authentication drop-ins for customer login and session management +- **Cart Integration**: Works with cart drop-ins to enable quote requests from cart contents +- **Order Management**: Connects with order drop-ins for quote-to-order conversion +- **Company Management**: Supports company-specific quote workflows and permissions + +## Getting Started + +Ready to implement quote management in your B2B storefront? Start with the [installation guide](/dropins-b2b/quote-management/installation/) to set up the basic functionality, then explore the other topics to customize the component for your specific B2B requirements. \ No newline at end of file diff --git a/src/content/docs/dropins-b2b/quote-management/initialization.mdx b/src/content/docs/dropins-b2b/quote-management/initialization.mdx new file mode 100644 index 000000000..3b3dda9e7 --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/initialization.mdx @@ -0,0 +1,98 @@ +--- +title: Quote Management initialization +description: Learn how to configure the initializer for the quote management drop-in component. +--- + +import OptionsTable from '@components/OptionsTable.astro'; + +The quote management drop-in component initializer provides options for configuring language definitions and specifying quote identifiers. + +## Configuration options + +The quote management component initializer accepts the following configuration options: + + + +## Example + +The following code shows an example implementation of the quote management initializer configuration: + +```ts +// Initialize quote management +initializeDropin(async () => { + await initializers.mountImmediately(initialize, { + langDefinitions, + quoteId: 'quote-123', + }); +})(); +``` + +## Set language definitions + +The `langDefinitions` property registers the quote management component dictionary files, which are used for internationalization purposes. These files allow you to provide localized text for different languages in your B2B application. + +```ts +// Fetch the dictionary files for your application +const en_US = await fetch('/i18n/en_US.json').then((res) => res.json()); +const fr_FR = await fetch('/i18n/fr_FR.json').then((res) => res.json()); + +// Register the component with language definitions +const langDefinitions = { + default: en_US, + en_US, + fr_FR, +}; + +// Initialize quote management with language definitions +initializeDropin(async () => { + await initializers.mountImmediately(initialize, { + langDefinitions, + }); +})(); +``` + +## Set quote ID + +The `quoteId` property specifies which quote should be loaded and managed by the component. This is required when you want to work with a specific quote. + +```ts +// Initialize quote management with a specific quote +initializeDropin(async () => { + await initializers.mountImmediately(initialize, { + quoteId: 'quote-123', + }); +})(); +``` + +## Complete example + +Here's a complete example showing all configuration options: + +```ts +// Complete quote management initialization +const initializeQuoteManagement = async () => { + // Fetch language definitions + const en_US = await fetch('/i18n/en_US.json').then((res) => res.json()); + const fr_FR = await fetch('/i18n/fr_FR.json').then((res) => res.json()); + + // Initialize the component + await initializers.mountImmediately(initialize, { + langDefinitions: { + default: en_US, + en_US, + fr_FR + }, + quoteId: 'quote-123', + }); +}; + +// Register and mount +initializers.register(initializeQuoteManagement); +window.addEventListener('load', initializers.mount); +``` \ No newline at end of file diff --git a/src/content/docs/dropins-b2b/quote-management/installation.mdx b/src/content/docs/dropins-b2b/quote-management/installation.mdx new file mode 100644 index 000000000..74e12bc3e --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/installation.mdx @@ -0,0 +1,288 @@ +--- +title: Quote Management installation +description: Learn how to install the quote management drop-in component into your B2B site. +sidebar: + order: 2 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; +import Tasks from '@components/Tasks.astro'; +import Task from '@components/Task.astro'; +import Callouts from '@components/Callouts.astro'; +import Vocabulary from '@components/Vocabulary.astro'; + +The quote management drop-in component is designed for the browser's JavaScript runtime without the need for a bundler. You can also install and execute the drop-in component in a build-time environment with bundlers like Vite. The installation steps for both runtime and build-time environments are the same after the initial drop-in component package imports. + +## Prerequisites + +Before installing the quote management drop-in, ensure you have: + +- Adobe Commerce B2B module installed and configured +- Negotiable quotes functionality enabled in your Commerce backend +- User authentication system in place +- Cart functionality available + +## Step-by-step + +Use the following steps to install the quote management component: + +:::note[Edge Delivery Service] +These steps are specific for EDS projects, and may not necessarily be the same for other frameworks such as a static HTML or React framework. +::: + + + + + +### Install the packages + +Use a CDN or NPM (recommended for performance) to install the drop-in component tools (`@dropins/tools`) and quote management (`@dropins/storefront-quote-management`) packages. + + + + + ```bash frame="none" + npm install @dropins/tools @dropins/storefront-quote-management + ``` + + + + + ```html title="index.html" del={"Replace with actual URLs":5-7} + + + + Your B2B Storefront + + + + + ``` + + + + +:::note[Install @dropins/tools] +All drop-in components require the `@dropins/tools` package. This package contains the libraries that drop-in components need to initialize and communicate, including `fetch-graphql`, `event-bus`, and `initializer` utilities. +::: + + + + +### Map the packages + +In the `` tag of your `index.html` or `head.html` file, use an `importmap` pointed to the `node_modules` directory, a **custom local directory**, or **CDN** (for run-time environments). + + + + This example shows an `importmap` added to a `head.html` The `importmap` points both packages to the local `node_modules` directory that contains your installed drop-in component files from the drop-in component tools (@dropins/tools) and the quote management (@dropins/storefront-quote-management): + + ```html title="head.html" + + + ``` + + + This example shows an `importmap` added to a `head.html` The `importmap` points both packages to local directories that contain all the optimized/minified files from the drop-in component tools (@dropins/tools) and the quote management (@dropins/storefront-quote-management): + + ```html title="head.html" + + + ``` + + + + This example shows an `importmap` pointing both packages to a CDN for the drop-in component tools (@dropins/tools) and the quote management (@dropins/storefront-quote-management): + + ```html title="index.html" {"Replace CDN URLs with correct URLs":5-7} del='https://cdn.jsdelivr.net/npm/' + + + + ``` + + + + + +With the `importmap` defined for both runtime and build-time environments, you can now **import the required files** from these packages into your quote management block as described in the next step. + + + + +### Import the required files + +Import the required files from the drop-in components tools (`@dropins/tools/fetch-graphql.js'`, `@dropin/tools/initializers.js`) and the quote management (`@dropins/storefront-quote-management`) into a JavaScript file for your quote management block. The example here shows the imports added to a `quote-management.js` file. These imports constitute the minimum imports you need to create a fully functioning quote management system for your B2B site: + +```js title="quote-management.js" +// GraphQL Client +import * as mesh from '@dropins/tools/fetch-graphql.js'; + +// component tools +import { initializers } from '@dropins/tools/initializer.js'; + +// drop-in component functions +import * as pkg from '@dropins/storefront-quote-management/api.js'; + +// Drop-in component provider +import { render as provider } from '@dropins/storefront-quote-management/render.js'; + +// Drop-in component containers +import RequestNegotiableQuoteForm from '@dropins/storefront-quote-management/containers/RequestNegotiableQuoteForm.js'; +import ManageNegotiableQuote from '@dropins/storefront-quote-management/containers/ManageNegotiableQuote.js'; +import ItemsQuoted from '@dropins/storefront-quote-management/containers/ItemsQuoted.js'; +import QuotesListTable from '@dropins/storefront-quote-management/containers/QuotesListTable.js'; +``` + + + +### GraphQL client + +Enables the ability to set and get endpoints and headers, as well as fetches data and configurations for quote management operations. + +### Drop-in component tools + +The Initializer is responsible for setting up event listeners and initializing the quote management module with the given configuration. + +### Drop-in component functions + +Enables the quote management API functions: requestNegotiableQuote, getQuoteData, getCustomerData, and more. + +### Drop-in component provider + +Renders the quote management UI components. + +### Drop-in component containers + +Structural elements that are responsible for managing and displaying the UI for quote request forms, quote management interfaces, and quoted items display. + + + + + + +### Connect to the endpoint + +Connect your quote management component to the API Mesh endpoint and set the required headers as shown in the example below. Replace the endpoint URL and header placeholder values with the actual values from your Commerce backend services: + +```js title="quote-management.js" +// Set endpoint configuration +mesh.setEndpoint('https:///graphql'); +``` + +#### Request headers + +The quote management drop-in requires specific headers for B2B functionality. The below example shows how to set the `Commerce-Auth` header with the customer token for the Authorization header, and how to set the store code header for store specific features. Replace the header values with the actual values from your Commerce backend services: + +```js title="quote-management.js" +// Set the customer token. This method is specific to @dropins/storefront-quote-management package. +pkg.setFetchGraphQlHeader('commerce-auth', ''); + +// Set store code header. This method is specific to the @dropins/tools package. +mesh.setFetchGraphQlHeader('store', ''); +``` + +:::note[Authentication] +Note that this setting is specific for GraphQL Mesh, so refer to the [Commerce GraphQL Service](https://developer.adobe.com/commerce/webapi/graphql/usage/headers/#request-headers) for direct connections. Visit the [user authentication](/dropins/user-auth/) drop-in component for instructions on setting up Authentication features. +::: + + + + +### Register and load the drop-in + +The code below shows how to register the quote management drop-in, load it (mount), and enable the logger for debugging purposes. You can add these functions within a ` +``` + + + +1. This function registers the quote management drop-in to be loaded on the page by the `initializers.mount` function. +1. This event handler triggers the initializers.mount function to load the quote management drop-in after the page has loaded. + + + + + + +### Render the drop-in + +Render the quote management components on the page. The example below provides the minimal configuration options required to render the quote management drop-in components: + +```js title="quote-management.js" +// Render Request Quote Form +provider.render(RequestNegotiableQuoteForm, { + routeProduct: (item) => { + return `${item.url.categories.join('/')}/${item.url.urlKey}`; + }, + routeCart: () => 'your-cart-element', +})(document.getElementById('your-request-quote-element')); + +// Render Manage Quote +provider.render(ManageNegotiableQuote, { + routeProduct: (item) => { + return `${item.url.categories.join('/')}/${item.url.urlKey}`; + }, + routeCart: () => 'your-cart-element', + routeCheckout: () => 'your-checkout-element', +})(document.getElementById('your-manage-quote-element')); + +// Render Items Quoted +provider.render(ItemsQuoted, { + routeProduct: (item) => { + return `${item.url.categories.join('/')}/${item.url.urlKey}`; + }, +})(document.getElementById('your-items-quoted-element')); + +// Render Quotes List Table +provider.render(QuotesListTable, { + routeProduct: (item) => { + return `${item.url.categories.join('/')}/${item.url.urlKey}`; + }, + onQuoteSelected: (quote) => { + console.log('Quote selected:', quote); + // Navigate to quote management page + }, +})(document.getElementById('your-quotes-list-element')); +``` + +Test the quote management functionality by viewing your quote management page in a browser, or running your local dev or build server. If you see the quote management components rendered in the browser, congrats!, you have a successful installation. If not, check the console for any errors and verify that you have followed all the steps correctly. + + + + + +## Summary + +The installation of the quote management drop-in follows the same pattern as other drop-in components: Install, Map, Import, Connect, Register, and Render. The key difference is the B2B-specific functionality and the requirement for proper authentication and permissions setup. \ No newline at end of file diff --git a/src/content/docs/dropins-b2b/quote-management/styles.mdx b/src/content/docs/dropins-b2b/quote-management/styles.mdx new file mode 100644 index 000000000..e6be02129 --- /dev/null +++ b/src/content/docs/dropins-b2b/quote-management/styles.mdx @@ -0,0 +1,491 @@ +--- +title: Quote Management styles +description: Learn how to customize the appearance of the quote management drop-in component. +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +The quote management drop-in component provides comprehensive styling options to match your B2B storefront's design system. You can customize colors, typography, spacing, and layout to create a cohesive user experience. + +## CSS Custom Properties + +The quote management drop-in uses CSS custom properties (variables) for easy theming. You can override these variables to match your brand colors and design system. + +### Color Variables + +```css +:root { + /* Primary colors */ + --quote-primary-color: #1976d2; + --quote-primary-hover: #1565c0; + --quote-primary-light: #e3f2fd; + + /* Secondary colors */ + --quote-secondary-color: #424242; + --quote-secondary-hover: #303030; + --quote-secondary-light: #f5f5f5; + + /* Status colors */ + --quote-success-color: #4caf50; + --quote-warning-color: #ff9800; + --quote-error-color: #f44336; + --quote-info-color: #2196f3; + + /* Background colors */ + --quote-background: #ffffff; + --quote-surface: #f8f9fa; + --quote-surface-hover: #e9ecef; + + /* Text colors */ + --quote-text-primary: #212529; + --quote-text-secondary: #6c757d; + --quote-text-muted: #adb5bd; + + /* Border colors */ + --quote-border-color: #dee2e6; + --quote-border-light: #e9ecef; + --quote-border-focus: var(--quote-primary-color); +} +``` + +### Spacing Variables + +```css +:root { + /* Spacing scale */ + --quote-spacing-xs: 0.25rem; + --quote-spacing-sm: 0.5rem; + --quote-spacing-md: 1rem; + --quote-spacing-lg: 1.5rem; + --quote-spacing-xl: 2rem; + --quote-spacing-xxl: 3rem; + + /* Component spacing */ + --quote-padding-sm: var(--quote-spacing-sm); + --quote-padding-md: var(--quote-spacing-md); + --quote-padding-lg: var(--quote-spacing-lg); + + --quote-margin-sm: var(--quote-spacing-sm); + --quote-margin-md: var(--quote-spacing-md); + --quote-margin-lg: var(--quote-spacing-lg); +} +``` + +### Typography Variables + +```css +:root { + /* Font families */ + --quote-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --quote-font-family-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace; + + /* Font sizes */ + --quote-font-size-xs: 0.75rem; + --quote-font-size-sm: 0.875rem; + --quote-font-size-md: 1rem; + --quote-font-size-lg: 1.125rem; + --quote-font-size-xl: 1.25rem; + --quote-font-size-xxl: 1.5rem; + + /* Font weights */ + --quote-font-weight-normal: 400; + --quote-font-weight-medium: 500; + --quote-font-weight-semibold: 600; + --quote-font-weight-bold: 700; + + /* Line heights */ + --quote-line-height-tight: 1.25; + --quote-line-height-normal: 1.5; + --quote-line-height-relaxed: 1.75; +} +``` + +## Component-Specific Styles + +### RequestNegotiableQuoteForm + +Customize the quote request form appearance: + +```css +/* Form container */ +.quote-request-form { + background: var(--quote-background); + border: 1px solid var(--quote-border-color); + border-radius: 8px; + padding: var(--quote-padding-lg); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +/* Form title */ +.quote-request-form__title { + color: var(--quote-text-primary); + font-size: var(--quote-font-size-xl); + font-weight: var(--quote-font-weight-semibold); + margin-bottom: var(--quote-margin-lg); +} + +/* Form fields */ +.quote-request-form__field { + margin-bottom: var(--quote-margin-md); +} + +.quote-request-form__label { + display: block; + color: var(--quote-text-primary); + font-weight: var(--quote-font-weight-medium); + margin-bottom: var(--quote-spacing-xs); +} + +.quote-request-form__input { + width: 100%; + padding: var(--quote-padding-sm); + border: 1px solid var(--quote-border-color); + border-radius: 4px; + font-size: var(--quote-font-size-md); + transition: border-color 0.2s ease; +} + +.quote-request-form__input:focus { + outline: none; + border-color: var(--quote-border-focus); + box-shadow: 0 0 0 2px rgba(25, 118, 210, 0.2); +} + +/* Form buttons */ +.quote-request-form__actions { + display: flex; + gap: var(--quote-spacing-md); + margin-top: var(--quote-margin-lg); +} + +.quote-request-form__button { + padding: var(--quote-padding-sm) var(--quote-padding-lg); + border: none; + border-radius: 4px; + font-weight: var(--quote-font-weight-medium); + cursor: pointer; + transition: background-color 0.2s ease; +} + +.quote-request-form__button--primary { + background: var(--quote-primary-color); + color: white; +} + +.quote-request-form__button--primary:hover { + background: var(--quote-primary-hover); +} + +.quote-request-form__button--secondary { + background: var(--quote-secondary-light); + color: var(--quote-text-primary); + border: 1px solid var(--quote-border-color); +} + +.quote-request-form__button--secondary:hover { + background: var(--quote-surface-hover); +} +``` + +### ManageNegotiableQuote + +Customize the quote management interface: + +```css +/* Quote management container */ +.quote-management { + background: var(--quote-background); + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +/* Quote header */ +.quote-management__header { + background: var(--quote-surface); + padding: var(--quote-padding-lg); + border-bottom: 1px solid var(--quote-border-color); +} + +.quote-management__title { + color: var(--quote-text-primary); + font-size: var(--quote-font-size-xl); + font-weight: var(--quote-font-weight-semibold); + margin: 0; +} + +.quote-management__meta { + color: var(--quote-text-secondary); + font-size: var(--quote-font-size-sm); + margin-top: var(--quote-spacing-xs); +} + +/* Quote status */ +.quote-status { + display: inline-flex; + align-items: center; + padding: var(--quote-spacing-xs) var(--quote-spacing-sm); + border-radius: 16px; + font-size: var(--quote-font-size-xs); + font-weight: var(--quote-font-weight-medium); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.quote-status--pending { + background: var(--quote-warning-color); + color: white; +} + +.quote-status--approved { + background: var(--quote-success-color); + color: white; +} + +.quote-status--rejected { + background: var(--quote-error-color); + color: white; +} + +/* Quote actions */ +.quote-actions { + display: flex; + gap: var(--quote-spacing-sm); + margin-top: var(--quote-margin-md); +} + +.quote-action-button { + padding: var(--quote-spacing-sm) var(--quote-spacing-md); + border: 1px solid var(--quote-border-color); + border-radius: 4px; + background: var(--quote-background); + color: var(--quote-text-primary); + font-size: var(--quote-font-size-sm); + cursor: pointer; + transition: all 0.2s ease; +} + +.quote-action-button:hover { + background: var(--quote-surface-hover); + border-color: var(--quote-primary-color); +} + +.quote-action-button--primary { + background: var(--quote-primary-color); + color: white; + border-color: var(--quote-primary-color); +} + +.quote-action-button--primary:hover { + background: var(--quote-primary-hover); +} +``` + +### ItemsQuoted + +Customize the quoted items display: + +```css +/* Items quoted container */ +.items-quoted { + background: var(--quote-background); + border-radius: 8px; + overflow: hidden; +} + +.items-quoted__header { + background: var(--quote-surface); + padding: var(--quote-padding-md); + border-bottom: 1px solid var(--quote-border-color); +} + +.items-quoted__title { + color: var(--quote-text-primary); + font-size: var(--quote-font-size-lg); + font-weight: var(--quote-font-weight-semibold); + margin: 0; +} + +/* Quote items list */ +.quote-items { + list-style: none; + margin: 0; + padding: 0; +} + +.quote-item { + display: flex; + align-items: center; + padding: var(--quote-padding-md); + border-bottom: 1px solid var(--quote-border-light); + transition: background-color 0.2s ease; +} + +.quote-item:hover { + background: var(--quote-surface); +} + +.quote-item:last-child { + border-bottom: none; +} + +/* Product image */ +.quote-item__image { + width: 60px; + height: 60px; + object-fit: cover; + border-radius: 4px; + margin-right: var(--quote-spacing-md); +} + +/* Product details */ +.quote-item__details { + flex: 1; +} + +.quote-item__name { + color: var(--quote-text-primary); + font-weight: var(--quote-font-weight-medium); + margin: 0 0 var(--quote-spacing-xs) 0; +} + +.quote-item__sku { + color: var(--quote-text-secondary); + font-size: var(--quote-font-size-sm); + margin: 0; +} + +/* Pricing */ +.quote-item__pricing { + text-align: right; + margin-left: var(--quote-spacing-md); +} + +.quote-item__price { + color: var(--quote-text-primary); + font-weight: var(--quote-font-weight-semibold); + font-size: var(--quote-font-size-lg); +} + +.quote-item__quantity { + color: var(--quote-text-secondary); + font-size: var(--quote-font-size-sm); + margin-top: var(--quote-spacing-xs); +} +``` + +## Responsive Design + +The quote management drop-in includes responsive design patterns: + +```css +/* Mobile styles */ +@media (max-width: 768px) { + .quote-request-form { + padding: var(--quote-padding-md); + } + + .quote-request-form__actions { + flex-direction: column; + } + + .quote-request-form__button { + width: 100%; + } + + .quote-management__header { + padding: var(--quote-padding-md); + } + + .quote-actions { + flex-wrap: wrap; + } + + .quote-item { + flex-direction: column; + align-items: flex-start; + } + + .quote-item__image { + margin-right: 0; + margin-bottom: var(--quote-spacing-sm); + } + + .quote-item__pricing { + text-align: left; + margin-left: 0; + margin-top: var(--quote-spacing-sm); + } +} + +/* Tablet styles */ +@media (min-width: 769px) and (max-width: 1024px) { + .quote-management { + display: grid; + grid-template-columns: 1fr 300px; + gap: var(--quote-spacing-lg); + } + + .quote-management__main { + grid-column: 1; + } + + .quote-management__sidebar { + grid-column: 2; + } +} +``` + +## Dark Mode Support + +Add dark mode support using CSS custom properties: + +```css +@media (prefers-color-scheme: dark) { + :root { + --quote-background: #1a1a1a; + --quote-surface: #2d2d2d; + --quote-surface-hover: #3a3a3a; + --quote-text-primary: #ffffff; + --quote-text-secondary: #b3b3b3; + --quote-text-muted: #808080; + --quote-border-color: #404040; + --quote-border-light: #333333; + } +} +``` + +## Custom Themes + +Create custom themes by overriding CSS variables: + +```css +/* Corporate theme */ +.theme-corporate { + --quote-primary-color: #1e3a8a; + --quote-primary-hover: #1e40af; + --quote-secondary-color: #374151; + --quote-font-family: 'Inter', sans-serif; + --quote-border-radius: 6px; +} + +/* Modern theme */ +.theme-modern { + --quote-primary-color: #6366f1; + --quote-primary-hover: #4f46e5; + --quote-secondary-color: #64748b; + --quote-font-family: 'Poppins', sans-serif; + --quote-border-radius: 12px; + --quote-spacing-md: 1.25rem; +} +``` + +## Best Practices + +- Use CSS custom properties for consistent theming +- Follow your design system's spacing and typography scales +- Test styles across different screen sizes +- Ensure sufficient color contrast for accessibility +- Use semantic class names for better maintainability +- Consider dark mode support for better user experience +- Test with different content lengths to ensure layout stability \ No newline at end of file diff --git a/src/content/docs/dropins/checkout/event-handling.mdx b/src/content/docs/dropins/checkout/event-handling.mdx index db563a2ae..200cabd0a 100644 --- a/src/content/docs/dropins/checkout/event-handling.mdx +++ b/src/content/docs/dropins/checkout/event-handling.mdx @@ -32,11 +32,13 @@ declare module '@adobe-commerce/event-bus' { 'cart/updated': CartModel | null; 'cart/reset': void; 'cart/merged': { oldCartItems: any[] }; + 'cart/data': CartModel | null; 'checkout/initialized': CheckoutData | null; 'checkout/updated': CheckoutData | null; 'checkout/values': ValuesModel; 'shipping/estimate': ShippingEstimate; authenticated: boolean; + locale: string; error: { source: string; type: string; error: Error }; } diff --git a/src/content/docs/sdk/reference/events.mdx b/src/content/docs/sdk/reference/events.mdx index 568234cd9..06afafed0 100644 --- a/src/content/docs/sdk/reference/events.mdx +++ b/src/content/docs/sdk/reference/events.mdx @@ -58,6 +58,8 @@ const cartListener = events.on('cart/data', (cartData) => { cartListener.off(); ``` +**Note:** The `cart/data` event is commonly used across drop-ins but may not be defined in all event type definitions. Always check the specific drop-in's event types for available events. + ### Emit Events Broadcast events to all listeners across your application.